Sunday 30 May 2010

Planet Shapes or The Bliss of Pointless Nerdy Hobbies

A couple of weeks ago I finally found a (tentative) solution for a problem that I have been mulling over since nearly 20 years. The stubbornness of this problem was only rivaled by its absolute pointlessness and obscurity. Let me elaborate.

RPG obsession

When I was 14 or 15 a friend of mine asked me an innocent question which turned out to have a huge impact on my life - he invited me to join his MERP group. I am not entirely sure whether I already had read Lord of the Rings at that time but I definitely had read the Hobbit, therefore although I totally did *not* get the concept of role playing games I was happy to give it a try.
This triggered an addiction-like fascination for role-playing games, Fantasy and Sci-Fi which only subsided years later. I spent countless hours reading F&SF novels (many of them crappy and most of them crappily translated) and playing MERP, Midgard, Warhammer and RoleMaster (sometimes three to four times a week). Luckily school in Germany usually ends at 13:00, therefore the amount of school hours I ended up sacrificing for my hobby were limited enough to let me get through school more or less successfully.

Reinventing

One peculiar thing about me being obsessed with something is that I usually very quickly begin to feel unsatisfied with the way things are. After a while I start to think 'this is really not good I could easily do this much better'. In many cases this is of course a blatant overestimation of my abilities (or an underestimation of the difficulty of the problem), but usually I end up spending many fun hours on some creative activity and I always learn a lot from it.
Anyways, in the case of the RPG obsession I of course immediately started wanting to design my own RPG system (I think I showed up at my third session of MERP with a new, "improved" character sheet, which was much more complicated and much less useful than the regular one) and my own fantasy world. Since starting small was never my thing this fantasy world of course had to have a complete history, its own biology, its own geology, ... you get the drift.
The problem

One problem I encountered early on was that I would have liked my hand-drawn maps to be an *exact* representation of my invented reality. I really hated the whole idea of having to live with a sub-optimal 2-dimensional projection of a 3-dimensional curved surface. There are some standard solutions to this problem - besides pragmatically accepting the imperfection of maps - such as ignoring it (usually done by fantasy authors), or assuming the world is a disk. However I wanted my world to be plausible with as little alterations to real-world physics as possible. I thought about many different solutions, from the dumb to the downright bizarre, but none of them was even close to satisfying. In the end I had to leave the problem unsolved.

The solution

Until three weeks ago, when I found this. After having believed for many years that the only way a planets shape can change with increasing angular momentum was to become an increasingly flatter oblate spheroid I learned to my utter astonishment that there are at least two other (slightly bizarre) possible shapes. Both of them would not solve my problem but greatly alleviate it. With a cigar-shaped planet the inaccuracies of the map are quite small (as long as you stay away from the ends of the cigar), but rotation speed and gravity should still occur in earth-like combinations. Outlandish but physically plausible - a great solution! Now I just have to find a way to actually calculate which combinations of rotation, density, mass and shape allow for earth-like conditions...

Parting words

As usual for me with these projects, after an initial time of furious activity my interest in my RPG world somewhat tapered off back then and the project never reached anything resembling completion (it reincarnated a while later as the setting for my own horribly complicated version of the board game Civilization). Luckily I am nowadays a bit wiser than when I was young and don't let myself be depressed by another unfinished project. I rather see fiddling around with one of them as an aimless entertaining activity which I pursue for its own sake not in order to produce something great.
And - as in this case - I usually learn something which is really obscure and pointless but fascinating.

Tuesday 4 May 2010

C++ sucks for simulations

Today I once again had to realize that C/C++ is really a bad language to use for numerical simulations. In two heavily scrutinized (and often used) pieces of code I found two simple, yet far-reaching bugs which would immediately have been spotted by the compiler or the run-time system, respectively, had I used a decent language.
I implemented the first version of the simulation I am currently (again) working on about six years ago. Since then I made countless changes and refinements, although the general structure remained the same (yay structured programming). In particular the core functions of the model class (which implement what the model actually does) I tend to revise quite often in an iterative process of checking results, trying to understand them and coming up with new ways to test whether what I come up with is what's actually happening.
The stable and general part of the code I am producing I usually pull into a private library (which is available from sourceforge but so much a work in progress that I won't link to it) after some time.
One of the bugs occurred in the core model, the other one in the library, both in pieces of code I have checked and re-checked dozens of times. Both bugs were really, really stupid.
I will keep the story of how I actually found these bugs in the end for another blog post, suffice it to say that one of them is a Heisenbug, i.e. it had no effect in the debug version of the program.

Bug 1:
1 const int sym_a = a.symmetry();
2 const int sym_b = b.symmetry();
3 enum {sym_random = 0, sym_noRoles = 1};
4 
5 const bool roleA = sym_a == sym_noRoles ? true : rng(2);
6 const bool roleB = sym_b == sym_noRoles ? true : 
7  (sym_a == sym_random ? !sym_a : rng(2));

Without going into too much detail, the basic idea behind this code is that two individuals a and b have to negotiate their "role" (roleA and roleB) in the conflict. How this negotiation happens depends on the mode of role assignment each individual prefers (sym_a and sym_b). In one particular mode (sym_random) b automatically has the opposite role of a. Therefore in line 7 it should be roleA instead of sym_a.
I know, it's just a typo and a stupid one at that (although the code shown here is slimmed down quite a bit, in the original version the mistake is a bit more difficult to spot). Still, one reason it can go unnoticed is that C++ happily let's me convert between enum, int and bool without as much as a cringe (BTW, the fact that I have to use int for sym_a and sym_b instead of an enum is due to another quirk of C++ - enums don't play well with IO).

Bug 2:

1 /** Gives true with a probability of p. */
2 bool choice(float p)
3  {
4  return (*this)() < Type((this->getMax()) * p);
5  }

Ok, this one is slightly embarassing. In a misguided attempt at optimisation I decided to rely on the input being valid (i.e. between 0 and 1). Which was sort of fine since in the original version I had two debug asserts checking for validity. Unfortunately it turned out that if I switched on SSE* code generation in gcc values of p==1.0 (which passed the asserts) lead to silent overflows in the multiplication so that the function always returned false. After I finally found it the bug was easily fixed by bypassing the comparison for values <=0 and >=1.

These two examples demonstrate two problems of C++ which make numerical programming significantly more error prone than necessary. Implicit conversion and silent arithmetic errors are by far not the most common sources of bugs in my programs. However if they occur the resulting bugs are among the hardest to find.
Unfortunately all languages that would offer enough static and dynamic checking to avoid these types of bugs come with a strong efficiency penalty (and, yes, 50% longer runtime *is* too much). It seems for now we will just have to make do with a bad language.