Evolution in action
At Resolver Systems, we've recently split into two teams; about two thirds of us work on the core Resolver One platform that is our main product (this group is inventively called the Platform team), and the other third build new spreadsheet/Python programs, using Resolver One, for specific clients' custom needs (the Apps team). This is great, because we are now not only building business solutions for people, as well as a generic platform (which means more money for us), but we are also dogfooding -- so we can be sure we're adding features and fixing bugs which really do help our users.
The problem with doing this is that everyone in the company has different preferences about how much time they want to spend in each team. Some people really like writing programs to fix business problems, and others are keener on abstract algorithms. We could have just said "stuff it" and swapped people around so that everyone was doing a 1:2 rotation, but it was much more fun to solve the problem in software :-) My aim was to somehow generate, for each of the next twelve iterations (the two-week development cycles we work in), a list of people who would form that iteration's Apps team and the people who'd form the iteration's Platform team.
So I put together a spreadsheet: an evolutionary algorithm for team scheduling.
If you're using Windows, you can download it and take a look (you can get a
free version of Resolver One to run it on if you haven't already).
You enter your team's preferences -- in terms of the percentage of time they'd
like to spend on the Apps team -- in the "Preferences" sheet (which also shows
some results from the last run), and then some numbers to guide the evolution (number
of generations, population size, etc) in the "Parameters" sheet, and then get the
best schedule it can generate in "Rota" sheet.
To be honest, it's using the spreadsheet more as a display mechanism than anything
else. But it's a fun bit of code, although I'm sure that anyone who actually works
on evolutionary algorithms would find it trivially simple (and probably broken :-).
The function GenerateSchedule
in the pre-formulae user code (for Resolver
newbies: in the box below the grid - the section with a green background) is the
interesting bit -- everything below there is just presentation logic. Here's how
it works:
- We generate a random set of schedules, each of which is created by picking three random people from our team and putting them into the Apps team, leaving the remainder in the Platform team.
- We then run through as many generations as the user specified. In each generation:
- Every schedule in our population is assigned a weight. This is generated
by a function called
WeightSchedule
, which is what people who study evolutionary algorithms would call a fitness function. Basically, the higher the number it returns, the less good the schedule is. - We sort the schedules by their weights, and then we kill off the worst of them.
- We then create a new generation comprising the survivors from the cull, and
a set of new schedules that are "parented" by those survivors, using the
function
MutateSchedule
. We apply a slight bias so that the fitter schedules have a better chance of reproducing than the others. - And on we go for another generation.
- Every schedule in our population is assigned a weight. This is generated
by a function called
WeightSchedule
was the most difficult function in the code to get right. (This
is in keeping with what I've heard about evolutionary algorithms in general.) Its
job is to return a number that is high for bad schedules, and low for good ones. I
found I got the best results by returning an arbitrary "high" value for any schedule
that failed to meet certain must-have criteria, and then working out, for each
person, the difference between the amount of time they wanted to spend in a given
team and the actual amount of time they spent there in the current schedule. I then
raised those per-person errors to the power of four (to make it clear that three
people 5% out is better than one person 15% out) and then summed the results. This
seemed to work just fine.
For MutateSchedule
I had a bit of fun. It's purpose is to generate a new child
from a single parent schedule (I chose to use an asexual reproduction model because,
in my experience, sexual reproduction and spreadsheets rarely mix well). My initial
implementation just switched one pair of people around for every iteration -- that is,
one person who was originally on the Apps team was now on the Platform team, and vice
versa. I then made the number of such swaps a user-settable parameter, so that people
could increase the extent of mutations. This sounded like a good idea, but didn't
help much -- indeed, increasing the number of swaps invariably made the system less
likely to produce a good schedule. My "background radiation" level was clearly too
high. So I then changed things so that you could specify a fractional number of
swaps. A swap level of 0.1 meant that each iteration has a one in ten chance of a
having someone swapped around. This seemed to work well -- indeed, 0.1 seemed pretty
close to the sweet spot for the number of swaps. I suppose this makes sense -- you can
imagine that a schedule with twelve iterations in it that is almost perfect is more
likely to be improved if you switch around two people in just one of its iterations
than if you make a swap for every iteration.
So that's it -- a simple evolutionary algorithm in a spreadsheet. I've deliberately not over-tidied the code in the version you can download above -- I've just sanitised the data so that no-one on the team's privacy is harmed, and then added a few comments for the more impenetrable bits of code. But it should all be pretty easy to understand, and I'd love to hear from anyone with comments (especially if they know more about this kind of thing than me...)
A bit of fun
This week's unofficial meme on the Unofficial Planet Python seems to be to name the programming languages you've learned. Here's Eric Florenzano's list (hat tip) -- it looks like the meme was started by Corey Goldberg -- and here's my list:
- BASIC (an odd ICL dialect, then Spectrum, Commodore, Amstrad, BBC, and QuickBasic)
- Z80 Assembler
- Pascal
- C
- Hypertalk (remember that?
Answer 'Are you sure?' with 'Yes' or 'No'. If it is 'Yes' then...
) - Logo
- Prolog
- LISP
- C++
- ML
- Modula-3
- Neil (proprietary, probably still in use at IST)
- Java
- JavaScript
- C#
- Python
Hmm. It looks like I've slowed down. Time to pick up that Erlang tutorial again...
Off to BoS
Tomorrow I'm off to Boston for the Business of Software conference, organised by Neil Davidson of Red Gate software and... hosted? Branded? In some obscure way connected to? ...the inimitable Joel Spolsky. I'm particularly looking forward to meeting those two characters; I was lucky enough to sit next to Neil at a Cambridge dinner earlier this year, and had a very interesting chat, and Joel is someone whose blog I've been following since before I knew the word "blog". For added fun, Alex Papadimoulis of the Daily WTF will also be there -- I have to wonder whether he's looking for ideas for further commercialising that site, or if he's building up the business that is his day job...
So, good attendees -- and a fantastic set of talks, with speakers ranging from Seth Godin to Richard Stallman. It should be great.
Resolver One as a Python Success Story
Jonathan Hartley, a friend who is also a developer at Resolver Systems, has contributed to the set of Python Success Stories at Pythonology.org with a description of how we've benefited from using Python -- in particular the .NET variant of the language, IronPython. It's well worth a read, especially if you're interested in how a Python-based Extreme Programming team can use the language for both its internal systems and its public product.
Workaround for Vista stupidity
When I run certain command-line tools from a command prompt in Windows Vista, it displays the results in a separate window. This separate window disappears when the tool exits. This is the most mind-bogglingly stupid behaviour I have encountered so far in an operating system famed for its mind-boggling stupidity. However, there is a workaround -- you need to start a shell as the Administrator user (not just as an Administrator).
Here's some more detail:
- If you are not the Administrator user (even if you are a member of the Administrators group) then when you run a command-line tool that requires admin privileges, you need to click on one of Vista's never-ending stream of "this program wants access to your computer" messages. This is pretty sensible, annoying though it can be.
- It then opens a new command-line window in which you can interact with the program. I can see no value in this whatsoever; the window is not highlighted in any way, so it's not to make it clear to you that this is a potentially dangerous program. Potential reasons welcome in the comments.
- The real stupidity, however, shows itself when the program exits. Because then
the window closes -- taking with it all of the information the tool displayed
for you.
foo /help
suddenly becomes totally useless. Error messages? Forget about them. - And to make it worse, if you try to redirect the standard output or error of the program to a file or to more or anything else, you get nothing -- it still goes into the disappearing window.
After spending quite literally hours trying to debug a problem with the Python
easy_install
script, which was quite sensibly logging the details of the problem
into a window that Vista promptly closed, I discovered a workaround:
H:\>runas /user:Administrator cmd
Enter the password for Administrator:
Attempting to start cmd as user "DRX\Administrator" ...
H:\>
And up comes a new command prompt. Anything you run in there will put the standard output and error into the command line it was started from, just as any sane user would have expected in the first place.
A Thinking Ape's Critique of Trans-Simianism
Klomp's primary argument rests on what he calls the 'Quickening,' an imagined point somewhere in the future when the advancement of 'culture' occurs so rapidly that its pace will far exceed that of biological evolution. In his own words,
"There will come a time when within a single generation we will develop one or possibly even two new ideas.... Current advancements in the 'bow' and 'arrow' industries suggest an exponential trend in the expansion of our technological capacities. We are able to perform hunts in a fraction of the time it took our ancestors, thus freeing up valuable time to 'think' of new ideas. In the post-simian world, we may develop into a species that is not only intellectually superior to our current state, but capable of feats beyond the comprehension of a contemporary simian."
Pardon this author for not holding his breath.
Making a fool of yourself in public
On the Business of Software Blog, Neil Davidson recommends using your fear of making yourself look stupid by failing publicly as a way to motivate yourself to work as hard as you need to work on your startup. Sounds right to me. When I was in my early 20s I saw the mortality rates for smokers and decided that I would give up at the age of 30. In order to make sure that I stuck to that, over the years I told pretty much every one of my friends that I was going to quit then, which meant that I really could not back down. The result is that on the night of my 30th birthday party I quit, and (bar one or two particularly drunken evenings) I've not touched a cigarette since.
Building a collection of classical music
I've been building up my collection of classical music recently, not least because Lola gave me a copy of Aaron Copland's excellent What to Listen for in Music for my birthday. It's interesting, poking around the different recordings by different musicians, and I was reminded of how hard it was when I first started buying classical music to understand the importance of getting the right recording of a particular piece.
It's silly -- because obviously I understood intellectually that one musician can play better than another. For people who've always loved the classics, whose parents brought them up on Beethoven, it seems ridiculous that someone might think that a collection of cheap recordings (like the Naxos ones I got at Uni) might be worth having. Why on earth would you want to listen to a second-rate recording? I think that the problem is that for someone brought up on pre-recorded pop music, it can seem like the recording is the composition. Or, to put it another way -- the original version, the version released on CD or iTunes or whatever, is the original version. The score, as it were.
For people like me, who grew up with pre-recorded music, let's spin that the other way; in classical music, obviously there is normally no original recording by the composer. Less obviously, this means that everything is a cover version, and just as with any piece of music, there are good covers (think, Jimi Hendrix playing All Along the Watchtower) and appalling covers (think, the Fratellis playing All Along the Watchtower). There is no original, no Bob Dylan version (which in the case of All Along the Watchtower might be a good thing, but that's a whinge for another day :-)
I'm not saying anything even vaguely ground-breaking or new here, but if someone had made the Watchtower comparison to me back in the mid-90s I probably could have saved a few quid on bad recordings of Prokofiev...
Off to visit the Beast of Redmond ;-)
Mahesh Prakriya at Microsoft was kind enough to suggest that I give a talk at the Lang.NET symposium, and so tomorrow I'm flying to Seattle. It looks like a fantastically interesting meetup, and I'm really looking forward to it.
The one hiccup for me was trying to work out what to put in the talk. Having been on so many client and potential client visits, and done marketing material for non-technical users, it was very hard to switch over to thinking again about what Mahesh had clearly realised, and Jon Udell touched on back when he did a screencast with us: that a lot of the power behind Resolver One comes from the way it treats spreadsheets as just another .NET language.
This doesn't mean that our marketing and sales efforts are wrong -- our users and users-to-be don't really care about how the program does what it does, they care about what problems it solves for them. But it's useful reminder to me that I need to keep both sides in mind.
[Update] The talk went well! It was videoed and I'll link to it as soon as they put it online. In the meantime, here are the slides.
[Update, later] Darryl Taft has written about the talk in eWeek.
Another YouTube screencast
Spent some time today on another screencast; this one's also up on YouTube, and looks pretty nice but isn't as clear as the last one. You can only just make out that my fake Web 2.0 startup has cashflow projections that would make a Bubble 1.0 e-commerce portal blush :-)