Sunday, December 21, 2008
Memes!
The 99 things meme
Things you’ve already done: bold
Things you want to do: italicize
Things you haven’t done and don’t want to - leave in plain font
1. Started your own blog.
Obviously. Well, I really did start it myself.
2. Slept under the stars.
As long as tents count, but not otherwise.
3. Played in a band.
A stretch. Pretended to play in my Sunday School band: this got me out of singing. Although I was supposed to have learned the recorder at school, I wasn't very good and I soon learned that I was less disruptive not actually blowing into the instrument. No further ambitions in this direction.
4. Visited Hawaii.
There are nice beaches where I live.
5. Watched a meteor shower.
Way cool.
6. Given more than you can afford to charity.
As wonderful as that sounds, I think it's counterproductive.
7. Been to Disneyland/world.
Over-hyped. I'd go somewhere that's all about the rides, not the characters. Nothing wrong with scary rides, though!
8. Climbed a mountain.
But one with a great view, not the kind that kills a few people every year.
9. Held a praying mantis.
These are great insects. My garden needs more of them.
10. Sang a solo.
It'd be fun, but I want a lot of practice first.
11. Bungee jumped.
I'd rather sky-dive; bungee jumping puts serious stress on your eyes and brain. I need mine.
12. Visited Paris.
Visiting cities isn't really my thing.
13. Watched a lightning storm at sea.
I'm surprised I've never done this. I want a sturdy boat, though.
14. Taught yourself an art from scratch.
One day I will compose some music; something for a synthesizer to play.
15. Adopted a child.
I would only inflict my parenting skills on one of my own.
16. Had food poisoning.
Never, that I'm aware of. Others have, and that's enough for me.
17. Walked to the top of the Statue of Liberty.
Nice view, I imagine, and I'd do it if I were ever in New York... but not an essential.
18. Grown your own vegetables.
Everyone should do this, if only to gain insight into how tough life is for farmers.
19. Seen the Mona Lisa in France.
Over-rated. It's a good painting, but it's a painting.
20. Slept on an overnight train.
I've fallen asleep on a commuter train. That'll have to do.
21. Had a pillow fight.
Who has not?
22. Hitch hiked.
I will plan to walk all the way, or I will organize my own transport.
23. Taken a sick day when you’re not ill.
My ambition is to take more sick days when I am ill.
24. Built a snow fort.
... but only if I find myself somewhere snowy.
25. Held a lamb.
Two of my uncles were farmers.
26. Gone skinny dipping.
27. Run a marathon.
I'd have to be a lot fitter than I am.
28. Ridden a gondola in Venice.
Seen pictures. Didn't look exciting.
29. Seen a total eclipse.
Yes, thanks to my geek parents. I was very young, though, and only just understood what was happening.
30. Watched a sunrise or sunset.
And who has not?
31. Hit a home run.
I'm going to claim this one, because I scored the winning goal in a soccer grand final and I think that's just as good.
32. Been on a cruise.
Seems like a fun thing to do, although I'd want one with a lot of scenery. A river perhaps?
33. Seen Niagara Falls in person.
I have photos to prove it. Which is good, because I don't remember it at all. I was one.
34. Visited the birthplace of your ancestors.
But only going back about five generations.
35. Seen an Amish community.
I've seen pictures.
36. Taught yourself a new language.
I'm assuming computer languages don't count.
37.Had enough money to be truly satisfied.
All it would take is just a little bit more. Actually, I've been pretty close for a lot of my life.
38. Seen the Leaning Tower of Pisa in person.
I've seen pictures.
39. Gone rock climbing.
Well worth it.
40. Seen Michelangelo’s David in person.
See "Mona Lisa".
41. Sung Karaoke.
See "Singing Solo".
42. Seen Old Faithful geyser erupt.
See "Leaning Tower of Pisa".
43. Bought a stranger a meal in a restaurant.
Actually, it's more that I'd like to find a stranger that I thought needed it, and actually do it.
44. Visited Africa.
Tourism just isn't my thing, is it?
45. Walked on a beach by moonlight.
Many holidays at the beach.
46. Been transported in an ambulance.
More trouble than it's worth.
47. Had your portrait painted.
I had a silhouette cut, but that's as far as it goes. I have many photos, and that's a far superior technology.
48. Gone deep sea fishing.
Rather go coastal sight-seeing. Fishing is fine, but not an ambition.
49. Seen the Sistine chapel in person.
No, I'm just not a tourist.
50. Been to the top of the Eiffel Tower in Paris.
I have seen photos of the view. It's splendid.
51. Gone scuba diving or snorkelling.
For my birthday. Great fun.
52. Kissed in the rain.
53. Played in the mud.
Whenever I could, as a child.
54. Gone to a drive-in theatre.
Back when they still existed.
55. Been in a movie.
Only if I actually cared about the movie, though. I've been on TV a couple of times... if you look carefully.
56. Visited the Great Wall of China.
Still not a tourist.
57. Started a business.
More as an exercise than a real career, though.
58. Taken a martial arts class
I am a yellow belt. This is not a very scary qualification.
59. Visited Russia.
What's with the tourism?
60. Served at a soup kitchen.
61. Sold Girl Scout cookies.
Scout job week, which I'll claim is the same. Hated it. Rather work than sell.
62. Gone whale watching.
Although I suspect I would be disappointed.
63. Gotten flowers for no reason.
64. Donated blood.
Many times.
65. Gone sky diving.
Oh yes. Glorious.
66. Visited a Nazi Concentration Camp.
I would think that much more worthwhile than the Great Wall etc. Although (because) the memories would stay a lot longer.
67. Bounced a cheque.
Accidentally - when I moved the funds in to cover it, I forgot to hit confirm. D'Oh!
68. Flown in a helicopter.
I love aerial transport.
69. Saved a favorite childhood toy.
From what?
70. Visited the Lincoln Memorial.
...etc.
71. Eaten Caviar.
Yuck.
72. Pieced a quilt.
Not my thing.
73. Stood in Times Square.
74. Toured the Everglades.
75. Been fired from a job.
I think the owner actually thought I was involved in stealing cigarettes. I make a lousy 7-11 attendant.
76. Seen the Changing of the Guard in London.
77. Broken a bone.
Never! To my surprise, it's not like I've lacked for opportunity.
78. Been on a speeding motorcycle.
For some values of "speeding". Faster than I was entirely comfortable with, considering the track state and the security of my grip.
79. Seen the Grand Canyon in person.
80. Published a book.
This is very unlikely to ever happen; I'm a much better reader than a writer. But a man's reach should exceed his grasp, as they say.
81. Visited the Vatican.
Too much risk of providing revenue for them.
82. Bought a brand new car.
I may one day do this, but it's not an ambition, and all my cars so far have been second-hand.
83. Walked in Jerusalem.
I would rather read in the paper that they'd stopped killing each other.
84. Had your picture in the newspaper.
Don't think so. Don't really care.
85. Read the entire Bible.
All but Psalms and Proverbs. Turned me decisively away from Christianity as a religion.
86. Visited the White House.
87. Killed and prepared an animal for eating.
Fish.
88. Had chickenpox.
Too young to remember, thankfully.
89. Saved someone’s life.
Only if you count giving blood.
90. Sat on a jury.
I would be interested by that, but I wouldn't go so far as to say "want".
91. Met someone famous.
I went to school with a (now) well-known standup comic.
92. Joined a book club.
I think these are mostly ways to shovel out low selling books. I have never lacked motivation to read.
93. Lost a loved one.
The only people who do not, die young or friendless. But who actually wants to?
94. Had a baby.
95. Seen the Alamo in person.
96. Swum in the Great Salt Lake.
97. Been involved in a law suit.
My initial reaction was "No, no, no", but I'd love to provide a piece of the puzzle to knock off some bogus lawsuit. I just don't want to be plaintiff or defendant - our legal system isn't remotely trustworthy enough for that.
98. Owned a cell phone.
99. Been stung by a bee.
I once thought I had, but hindsight (and the lack of symptoms) tells me I just trod on the bee and we both ran for our lives.
This list needs fewer tourist items.
Thursday, October 23, 2008
And Walmart makes three
The good news is that they're turning them off because they've had enough and they're selling DRM-free music. Why? Why does Walmart do anything? There's money in it!
Since I never bought DRM laden malware, I don't suffer the consequences, but I do get the benefit of being able to now buy un-broken music.
I love the smell of burning DRM servers in the morning. Smells like... victory.
Friday, September 26, 2008
Picking a winner
Penalty shoot-outs are horrible things. They're quite artificial; there's nothing of the real beauty of soccer in them. No teamwork, no assessment of the movement of 22 people, none of the sleight of hand that a really delightful piece of soccer involves. There is a precision shot (if done well), a little bit of fakery (a penalty shot that goes where the keeper was standing a second ago is entertaining, if risky), but mostly it's a coin-flip. Did the keeper guess right?
In basketball, if you're good at the game, you'll be good at the overtime, and you'll win. In soccer, there's very little practice at taking penalties, except for shootouts. In a really good game of soccer, there's no call for it at all.
We need a different way. I've heard lots of people talk about the suggestion I'm about to make, and I've never heard anyone make anything like a good argument against it.
In extra time, we should take people off. Two immediately when extra time starts; two more at the changeover. Unlimited interchange of the original 11; you can also replace someone from the 11-man team if you have ordinary subs left, but that is a permanent change. Sendoffs remove someone from the 11. I think you'd need a rule like: 2-card red means you're off the 11 without replacement; straight red means you're off the 11 and so is someone else. There must be a serious disadvantage to taking out the opposition's star player.
That will focus on soccer skills. Double-teaming that brilliant forward now becomes a lot harder. If your team has the passing game, you can wear the opposition out or simply leave a couple of defenders behind. Every player becomes vital, and with no backups a breakaway is much more likely to get a goal. The interchange rule means that rested players will periodically come on and run the legs off tired defenders.
It won't guarantee a result; there is no guaranteeing a result. But it will reduce the chance of a penalty shootout, and that has to be a good thing.
* I grew up in a country where the name "football" was already taken, ironically enough by games whose players did not kick the ball nearly as much. Some people get upset when football is called "soccer", but it is completely unambiguous and it's the name I know it by. Get over it.
Friday, September 19, 2008
Tell me more
So I've been looking at all the competing brands. It's been a while since I shopped for serious AV gear (to the extent that I ever have... an audiophile would shake his or her head sadly at the sight).
After some experience with this, I find that the bar has been raised. I used to be OK with going to a shop and looking at the boxes, asking the salesman questions etc. Not any more. If you don't have a website with a downloadable manual for the thing that I can read at home and compare with all the others - you don't make the cut.
And it had better be a complete manual, too. If you've got a feature, tell me about it. If I have to reply on the salesman, I'm not going to hear it. Partly because I'm at home surfing instead of in a shop talking, and partly because when I do talk to a salesman there's a good chance I'll know more than s/he does. Judging by recent experience, the salesman may not even know that the store stocks your brand.
Don't lie in your manual, either. The web will find out, and they will not be gentle.
It's the information age, and I love it. But some companies have had trouble catching up.
Friday, September 12, 2008
Olympic sports
- The walking; like a "world's tallest dwarf" competition, this one looks for the person who can jog while appearing to be walking. Slow-motion footage shows they're all cheating (technically), and who would actually try to get somewhere that way? If you're fit enough to go that fast, you'll run. Drop it, add a 20km running distance, and let them all join the real athletes
- Synchronized swimming. Who cares? It's a relic of old black and white musicals.
- Tennis and soccer. They have their own big events. Get someone in who will appreciate it.
- Archery. Don't get rid of it, just make them use a real bow instead of something that looks like it was stolen from a cell-phone tower.
- Dressage. "The Olympics are for everyone, not just someone who happens to own a dancing horse." (shamelessly stolen).
- Blah blah blah. You can get this stuff anywhere.
But then I had an epiphany: what's the problem? If people are happy to pay for these events, put 'em on. Add stair-climbing, or thumb-wrestling, or whatever. No, the decision that needs to be made here is, what are the Olympics about?
Are they about the canonical sports? The events that everyone should think about learning? Running, jumping, swimming, throwing, cycling; short distances and long. On this view, horse riding would once have been there, but no longer (dressage never would have, but whatever). Rowing is dubious. Soccer makes it in. High diving does not. Archery and fencing are gone, and shooting replaces them. Synchronized swimming should never have been invented, much less competed in; get those people into the gymnastics.
Or are they about all the sports? Should we just draw a line and say "this many registered players, and you're in"? Maybe so. If I don't like the dressage, I can just change channels (or even better, go outside and do something productive) ; next Olympics I'll probably be able to choose my own sports to watch. Even now, Tivo will let me fast-forward stuff I'm bored by.
I have a theory about sport: if we ever manage to perfect the world, it and the weather will be the only reason to have newspapers. Imagine the rest: business - Dow Jones up .01% for 374,023rd day running. Politics: government services still running smoothly. Crime: 386,000th day without crime. Sport: football, baseball, athletics, cricket, you name it.
So I'm gonna go with "get 'em all in". And next time I see an Olympic sport that bores me to tears, I'll leave you all to watch it in peace. As long as you leave me to read in peace.
Friday, August 01, 2008
DRM: Still failing
Microsoft quickly changed its mind after a backlash; Yahoo! have decided to refund its customers instead. Good for them, and hopefully they will learn the lesson that DRM schemes are broken by design. They do absolutely nothing but stop people from using the product.
These two companies have had to backtrack, but what of other, smaller companies? What happens when you don't even realize the servers have gone until far too late? I have a computer at home that is ten years old; it's still working fine. If I had bought music from a company ten years ago and it stopped providing service, I might not find out until I replace the computer!
What's the real problem? The DMCA prevents people from distributing the tools to fix such outages. Most such schemes are cracked quickly, because you can't distribute the players without giving people ways to break the encryption. Even putting it in your own sealed hardware doesn't help against determined customers, as Microsoft have found out. So the only thing that stops customers fixing it themselves is the law.
If we are to keep this DMCA, it needs to be rebalanced towards consumers: any time a copyright protection scheme fails to be maintained with at least the accessibility it had when selling the original materials, it should become legal to make and distribute software to bridge that gap. Turn your servers off, and full cracking software becomes instantly legal. Run them unreliably (at your end), and software that makes the materials accessible for at least the length of the outage becomes legal. No way to make them available without completely unprotecting them? Well, that's the limitation of the technology you chose.
The point of copyright is to increase the amount of material that is available for people to buy and use. DRM breaks that, and does it in long-term and ridiculous ways. I hope that the companies that make it eventually get the consumer message.
In the meantime, yet another reason to vote with your wallet.
Sacrilege!
Adding a page of "The God Delusion" to the pile was a nice touch, though. I imagine someone out there has made the argument that this shows he thinks of "The God Delusion" as a sacred text and Richard Dawkins as a prophet; to that person I say "I am laughing at you".
I think the much more interesting piece of the post was the discussion of the history of cracker desecration. I did not realize that this had been an actual charge levelled at Jews in the past - and that people had been exiled or even killed for it! That's a disgrace, and it shows just how far civilization has come since then. Although I must say that it doesn't lower my opinion of the Catholic Church's history at all, which speaks volumes for my current view of it.
The odd thing is that despite my low opinion of Catholicism as an institution; and despite thinking that the institution's faults are well-grounded in Christianity's history and sacred texts... I still think that a good Christian should be as horrified as I am by this, and on purely theological grounds, rather than mere common humanity.
The Christianity that I was raised on is a non-violent religion, one that turns the other cheek, and seeks to persuade by example and moral suasion rather than by force. The Christians themselves may fight to defend themselves and their country, but this is seen as a necessary evil to preserve life, not a positive virtue (as distinct from courage in the fight itself).
I have long thought that the punishment for sacrilege and blasphemy should be reserved to God himself; he is the one offended, and if He doesn't see fit to send lightning bolts or a rain of frogs, who am I to go further? Anyone attempting to impose their own punishment should be seen as faithless and a usurper.
So if PZ is struck by lightning in the next little while, you can draw your own conclusions. Or, contrariwise, if he's not.
Friday, July 25, 2008
Having It Both Ways
Let us leave aside the likelihood that when Muslims commit violence on other people in the name of their beliefs, these Catholics would be the first to comment that Islam is incompatible with civilization or whatever. Although I suspect that is true, I can't point at anyone saying both things. And there really are some Christians who argue that insulting religion (any religion) is wrong (thankfully, others do not).
I am also going to skip arguing about whether Muslims really are more violent in defense of their religion than Christians; there's a lot of mixed evidence there, and the answer will depend on where and when you are. All I know is what I read in the papers; and I would be more confident conducting a Koran reading in St Peter's Square than a Bible reading at the Dome of the Rock.
So I'm going to assume that desecrating a Koran is more dangerous than desecrating a communion wafer (although PZ is most likely physically a lot closer to any Catholic nutcases than he is to Muslim nutcases). And I'm going to assume (against the actual evidence) that PZ would therefore choose to do one and not the other.
This is a comment about Islam, not about PZ. It's perfectly reasonable to choose not to do one thing that's more dangerous than another. I might be 100% behind your right to describe some religion as false and ridiculous; and support prosecution for anyone who harmed you for it; but I'd still recommend against saying it outside their main temple if they had a reputation for violence. Congratulations if you would anyway, but if you decide to pick another battleground, that's probably a wise choice.
If Catholics are more peaceable than Muslims, that's to their credit. You will often hear criticism of Islam that it is not tolerant of other religions and other viewpoints, or that it is incompatible with democracy. The Catholic church has come a long way since it burnt people over theological disputes, but you don't get that credit and still demand the intimidation factor of a more violent religion.
Frankly Catholics would have looked a lot better just ignoring PZ (as most of them no doubt did). For all that he looks like a flag-burner, many of the Catholics attacking him look much worse.
Friday, July 18, 2008
Symbol Wars
I'll skip lightly over the specifics, except to note that I'm unlikely to be sympathetic to the Catholic side of this argument. I am, after all, not just an atheist, but a protestant atheist. And they are complaining that somebody held onto a communion wafer for several days instead of... eating it. This is a hate crime? If the KKK would confine themselves to illegally downloading hip-hop tracks and conspicuously not playing them, the world would be a better place.
So I am sympathetic to PZ's wish to ridicule them. They deserve it. And yet... deliberately abusing a communion wafer is a flag-burning. Communion does no harm to PZ- it's mostly done in churches, in semi-private; it doesn't wake people up like church bells, or kill people like making condoms illegal. Or even damage them like hidden sexual abuse.
I don't know what PZ plans to do with the wafers he's being sent, but whatever it is the message will most likely be one of pure hostility - he'll look like a jerk. Yes, communion is silly; but let's confine the argument to areas where the church does real harm. Publicly ship condoms to Africa, for example. Attacking the church for things it does wrong sends a message "leave the rest of us alone". Attacking it just for being itself sends a message "we know where you live".
Of course, the people threatening the original wafer-taker (I hesitate to call him a thief; the church was handing these things out, and just didn't approve of his use of it) are of course much worse than PZ. He's not threatening anyone, except perhaps for the Eucharist, which as a representative of an omnipotent deity can probably take care of itself. Especially given it was expected to be eaten. And there's no way PZ deserves to be sacked for this, much less threatened with physical harm.
All that said, I'll be interested to see what PZ does do. I hope it's something genuinely creative; something that actually makes people think, instead of some clumsy toilet-flushing incident.
Friday, July 04, 2008
And you're buying that?
There are a wide variety of frames out there at varying prices. Some of this makes sense - plastic rather than metal, steel rather than titanium, lightweight rather than heavy (i.e., better engineered to get the same lifetime with less weight). There's a certain amount "paying for the brand" here, no doubt - same as in any other consumer field. Xyxenhugl Corp may make just as good a product as well known brands, but most people will want some dividend for their bet.
And then there are the fashion glasses. These cost upwards of twice as much for no discernible difference in construction. That would still make sense if they were beautifully and stylishly crafted. But.... they're not.
My favorite in the group were a pair of sunglasses that had their maker's name picked out on the lenses in Bedazzler sequins. You might as well just have "stupid fashion victim" tattooed on your forehead; at least that wouldn't block any of your vision.
But, people buy this rubbish. A week later at work, I saw someone wearing an almost identical pair - these at least had the name written on the sides rather than the lenses. It still looks like you've let your 6-year-old kid design them for you, and the only thing it conveys to this curmudgeon is "look at me, I had more money than sense!". Because of course, now you have less money than you used to, because you paid far too much for your glasses.
Ironically enough, as I type this I am wearing a t-shirt given to me as a present a couple of years ago by my sister-in-law. It has the name of a well-known sportswear brand in large letters across the front. In my defense, I will only say that it's a functional shirt, and I paid no extra for it.
I think a lot of this stuff is equivalent to the tail of the peacock - status symbols, and the more expensive the better. I do wonder if the engineering mindset - emphasizing superfunctional feathers and showing off by flying really fast - is just another strategy. Or in my case, pointing out the poor aerodynamics of peacock tails and remarking how one is too smart to lumber oneself with such a thing.
Certainly it is just one of the ObSpouse's attractive qualities that I can be perfectly confident that she would never pay good money for such a poorly designed piece of advertising hype.
Peacock's tails at least have the redeeming quality of being intrinsically attractive, which is far too rare in fashionable clothing over the centuries. After all, if it looked good in and of itself, everyone would have it already, and it wouldn't be fashionable, now would it?
You people are not quite sane.
Friday, June 27, 2008
Vote for, not against
First, John Mcain's reaction to the habeas corpus ruling:
We are now going to have the courts flooded with so-called, quote, Habeas Corpus suits against the government, whether it be about the diet, whether it be about the reading material. And we are going to be bollixed up in a way that is terribly unfortunate, because we need to go ahead and adjudicate these cases.
And here's Barack Obama's reaction:
Chicago, IL - "Today's Supreme Court decision ensures that we can protect our nation and bring terrorists to justice, while also protecting our core values. The Court's decision is a rejection of the Bush Administration's attempt to create a legal black hole at Guantanamo - yet another failed policy supported by John McCain. This is an important step toward reestablishing our credibility as a nation committed to the rule of law, and rejecting a false choice between fighting terrorism and respecting habeas corpus. Our courts have employed habeas corpus with rigor and fairness for more than two centuries, and we must continue to do so as we defend the freedom that violent extremists seek to destroy. We cannot afford to lose any more valuable time in the fight against terrorism to a dangerously flawed legal approach. I voted against the Military Commissions Act because its sloppiness would inevitably lead to the Court, once again, rejecting the Administration's extreme legal position. The fact is, this Administration's position is not tough on terrorism, and it undermines the very values that we are fighting to defend. Bringing these detainees to justice is too important for us to rely on a flawed system that has failed to convict anyone of a terrorist act since the 9-11 attacks, and compromised our core values," said Barack Obama.
We must not only defend civilization: we must retain a civilization worth defending. If Obama turns out to be lying in his teeth, I'll be disappointed; but at least I can express a preference for the principles he claims to represent, over those the Republicans are actually representing.
Four more months.
Friday, June 20, 2008
Stupid UI Tricks
A little play found that no slip was required. We had installed Windows Messenger, and it had installed a Shared Folders icon in explorer. If you drag files across that icon, and you aren't logged in, it pops up a warning dialog. Not "if you try to drop files on it" - "if you drag files across it". The dialog steals focus, too, so your drag operation is dead. The freeze appears to be an ordinary bug, but the popup is apparently by design. What were they thinking? This is basically a black hole on your desktop - drag across it and you have to start again. Not what I wanted!
Obvious solutions: log in to Windows Messenger at all times (apparently the preferred option), or get rid of the folder. Well, Messenger can go jump; I'll log in when I'm good and ready, thanks. So how do you remove it?
Well, Messenger's Options dialog has a tab for the Shared Folders. Sadly, turning everything off does not remove the icon, even after a reboot.
Fortunately, Google knows everything. A number of sites tell you this:
Go looking in your installation for a file with a name like "C:\Program Files\MSN Messenger\fsshext.8.0.0787.00.dll". It might be in "Windows Live" instead of "MSN Messenger", depending on how you installed. Then go to Start - run, and do this:
RegSvr32 /u "C:\Program Files\MSN Messenger\fsshext.8.0.0787.00.dll"
Done. But what was that about? An annoying trap for you, only removable by hacking into your registry. What do you do if you have no confidence playing with your registry, and just want your computer to do as it's told? That folder should be easily removable from the UI; and it certainly shouldn't grab your cursor as it goes past.
Friday, June 13, 2008
Imagine Fair Use
I didn't want them to lose that particular lawsuit partly because it would allow them to play martyr, which would distract people from thinking about what they were actually saying; and I didn't want some big music company getting another win in their war on the general public.
So I was happy to see Lawrence Lessig get involved; and I was happy to see them win.
Remember, just because they're bad guys doesn't mean they're always wrong, and it doesn't mean they're not entitled to the same freedoms as the rest of us.
Saturday, June 07, 2008
DRM: Epic Fail
So what happens on August 31, 2008? On that day, Microsoft will turn off the servers that they maintain for the sole purpose of validating that the songs that people have already “purchased” through MSN Music are still theirs to play. Those people (hereafter “the victims”) will not notice the change right away. The victims will only notice it when they purchase a new computer, or when they upgrade the operating system on their current computer, or when the hard drive in their computer dies and needs to be rebuilt/reinstalled. At that point — transferring the music files they have “purchased” to another drive or a new computer — the Microsoft music player running on the victim’s PC (like iTunes, but all Microsoft-y instead of Apple-y) will make a call to Microsoft’s validation servers to verify that the music files were legitimately purchased. This call will fail, since the servers are not responding, since Microsoft has intentionally turned them off. The Microsoft music player will then conclude, incorrectly but steadfastly, that the music files were downloaded illegally and that the victim is a filthy pirate, and it will refuse to play them. In this case, the left hand knows exactly what the right hand is doing: they’re both giving you the finger.
I don't like DRM much. And this is one of the risks of DRM - the company that runs it drops support, and now you have music that you can't play and can't extract (either for legal reasons, or technical ones). Microsoft's advice is apparently to burn a CD and re-rip it. If you can do that, it wasn't very effective DRM in the first place; if you lose any quality in the process then you've still been bitten by DRM; and if you don't realize until you try to move your music off a dead system, then you're out of luck, and the only way to get your music back is to get a new copy from a file-sharing site. Bonus points for getting a better-quality copy or getting sued by the RIAA.
I didn't expect this to happen to Microsoft customers, and certainly not quite so soon, but I thank Microsoft for doing this. I can thank them, because I don't have any of their music, because I don't like DRM much; instead, they've provided the good guys (that is, the music buying public) with a perfect example of why you should not buy DRM-infested music.
Simple lesson for music companies: sell music that just plays because it's in an open format like the CD. Make it easier to find, buy, and play than to pirate, and people will cheerfully pay for it. They are paying for it. Make it harder to buy legitimately than pirate, and they'll pirate and hate you for complaining.
Haldane's Dilemma - trial code
DNASTORE.H:
#ifndef DNASTORE_H
#define DNASTORE_H
#include <iostream>
#include <fstream>
using namespace std;
class DNAStore
{
public:
DNAStore(int aTraits, int aMutations, int aGoodMutations,
int aBadMutations, double aInc, double aFitness);
// For file load operations
DNAStore();
// Reset the store's contents
void reset(int aTraits, int aMutations, int aGoodMutations,
int aBadMutations, double aInc, double aFitness);
friend ostream& operator<<( ostream& os, DNAStore& dna );
friend istream& operator>>( istream& os, DNAStore& dna );
// Compilation constants
enum {
// Max number of selectable traits to have
TRAITS = 23,
// How many copies of gene in the DNA
// Note that you can't just change COPIES and expect
// everything to work!
COPIES = 2,
// How many children per critter
// Note that if you change CHILDREN, you need to fix up
// the ages that Critters reproduce at.
CHILDREN = 2,
// Base count of critters in a generation
CRITTERS = 1000000,
// How old a critter has to be before reproducing
MATURE = 17,
// Years between children
CHILD_GAP = 6,
};
void summary();
//Run a trial for a setup
void runTrial();
// Print some mutation info
void printStats();
protected:
// Constants derived from the public ones; leave these alone.
enum {
// Variance in reproduction age
CHILD_VAR = CHILD_GAP / 2,
// Average length of a generation
GEN = MATURE + CHILD_GAP * (CHILDREN - 1) / 2,
// Maximum age of a critter
MAX_AGE = MATURE + CHILD_GAP * (CHILDREN - 1),
// Age at which a critter dies
DEAD = MAX_AGE + 1,
// Size of the critter array; needs to hold everyone at once
MAX_CRIT = CRITTERS * 3,
// Normal beneficial mutation level
BENEFICIAL = 1,
};
// Reproduce a kid from two parents
void reproduce(int aKid, int aP1, int aP2);
// Net beneficial mutations for this critter.
int fitness(int aPos);
// Advance a year with all the reproduction involved
void nextYear();
// Print stats and see if we should stop yet
bool analyze();
// Check dates and times; pause if necessary
bool pauseExecution();
// Pick a number between 0 and max
int choice(int aMax);
// Generate a random number between 0 and 1
double survivalChance();
// Rate of increase, must be <= CHILDREN
double mInc;
// Fitness improvement per beneficial mutation
double mFitness;
// Number of bad mutations per good one
int mBadMutations;
// Number of good mutations per generation
int mGoodMutations;
// How many years have gone by
int mYears;
// How many traits to actually use
int mTraits;
// How many mutations have been substituted
int mMutations;
// How many mutations need to be substituted
int mMaxMutations;
// What day was the last save done?
int mLastSave;
// The array of critter DNA
signed char mDNA[MAX_CRIT][COPIES][TRAITS];
// Which critter is the first one younger than this;
// Note that the youngest are at the end, but the storage
// is circular.
int mAges[DEAD + 1];
// Debugging variables
double mFitSum;
double mFitCount;
};
ostream& operator<<( ostream& os, DNAStore& dna );
istream& operator>>( istream& os, DNAStore& dna );
#endif
DNASTORE.CPP
#include <stdlib.h>
#include <iostream>
#include "dnastore.h"
#include "math.h"
#include "time.h"
#include "unistd.h"
#include "assert.h"
#define MAX(a, b) (a <= b ? b : a)
DNAStore::DNAStore(int aTraits, int aMutations, int aGoodMutations,
int aBadMutations, double aInc, double aFitness) :
mTraits(aTraits), mMaxMutations(aMutations),
mGoodMutations(aGoodMutations), mBadMutations(aBadMutations),
mInc(aInc), mFitness(aFitness), mLastSave(-1)
{
reset(aTraits, aMutations, aGoodMutations, aBadMutations,
aInc, aFitness);
}
// Reset the store's contents
void DNAStore::reset(int aTraits, int aMutations, int aGoodMutations,
int aBadMutations, double aInc, double aFitness)
{
mYears = 0;
mMutations = 0;
mTraits = aTraits;
mMaxMutations = aMutations;
mBadMutations = aBadMutations;
mInc = aInc;
mFitness = aFitness;
mLastSave = -1;
mGoodMutations = aGoodMutations;
// Initialize the array
for (int i = 0; i < MAX_CRIT; i++)
{
for (int j = 0; j < COPIES; j++)
{
for (int k = 0; k < TRAITS; k++)
{
mDNA[i][j][k] = 0;
}
}
}
// Start off a number of critters
for (int i = 0; i < DEAD + 1; i++)
{
mAges[i] = CRITTERS - ((i * CRITTERS) / DEAD);
}
// One beneficial mutation of each type to start
for (int i = 0; i < mTraits; i++)
{
mDNA[CRITTERS - 1 - i][0][i] = 1;
}
mFitSum = 0;
mFitCount = 0;
}
DNAStore::DNAStore() : mLastSave(-1)
{
// Expect a file read to fill all this in.
}
ostream& operator<<( ostream& os, DNAStore& dna )
{
os << dna.mInc << endl;
os << dna.mFitness << endl;
os << dna.mGoodMutations << endl;
os << dna.mBadMutations << endl;
os << dna.mYears << endl;
os << dna.mTraits << endl;
os << dna.mMutations << endl;
os << dna.mMaxMutations << endl;
for (int i = 0; i < dna.DEAD + 1; i++)
{
os << dna.mAges[i] << " ";
}
os << endl;
for (int i = 0; i < dna.MAX_CRIT; i++)
{
for (int j = 0; j < dna.COPIES; j++)
{
for (int k = 0; k < dna.TRAITS; k++)
{
os.put(dna.mDNA[i][j][k]);
}
}
os << endl;
}
os << endl;
return os;
}
istream& operator>>( istream& is, DNAStore& dna )
{
is >> dna.mInc;
is >> dna.mFitness;
is >> dna.mGoodMutations;
is >> dna.mBadMutations;
is >> dna.mYears;
is >> dna.mTraits;
is >> dna.mMutations;
is >> dna.mMaxMutations;
for (int i = 0; i < dna.DEAD + 1; i++)
{
is >> dna.mAges[i];
}
is.get(); // space
is.get(); // newline
for (int i = 0; i < dna.MAX_CRIT; i++)
{
for (int j = 0; j < dna.COPIES; j++)
{
for (int k = 0; k < dna.TRAITS; k++)
{
dna.mDNA[i][j][k] = is.get();
}
}
is.get(); // newline
}
return is;
}
// Reproduce a kid from two parents
void DNAStore::reproduce(int aKid, int aP1, int aP2)
{
int lBits = 0;
int c1 = 0;
int c2 = 0;
for (int i = 0; i < TRAITS; i++)
{
if (lBits <= 0)
{
c1 = rand() % 0x1000;
c2 = rand() % 0x1000;
lBits = 12;
}
// Choose one chromosome randomly from each parent.
mDNA[aKid][0][i] = mDNA[aP1][c1%COPIES][i];
mDNA[aKid][1][i] = mDNA[aP2][c2%COPIES][i];
c1 >>= 1;
c2 >>= 1;
lBits--;
}
}
// Net beneficial mutations for this critter.
int DNAStore::fitness(int aPos)
{
int retval = 0;
for (int i = 0; i < mTraits; i++)
{
retval += MAX(mDNA[aPos][0][i], mDNA[aPos][1][i]);
}
return retval;
}
// Pick a number between 0 and max
int DNAStore::choice(int aMax)
{
// Build a random number that's much bigger than aMax
// NB: Assumes RAND_MAX is 0x7fff as per g++
int myrand = rand();
int lNeed = aMax << 4;
while (lNeed > RAND_MAX)
{
lNeed >>= 15;
myrand = myrand << 15 + rand();
}
return myrand % aMax;
}
// Generate a random number between 0 and 1
double DNAStore::survivalChance()
{
double retval = (((rand() % 0x1000) << 12) + rand() % 0x1000);
retval = retval / 0x1000000;
mFitSum += retval;
mFitCount++;
return retval;
}
// Advance a year with all the reproduction involved
void DNAStore::nextYear()
{
// What fraction over or under the standard population are we?
int pop = mAges[0] - mAges[DEAD];
double lCrowding = (pop - CRITTERS) * mTraits;
lCrowding /= CRITTERS;
// Count all the kids that are going to be born this year
int lKids = mAges[MATURE] - mAges[MATURE + CHILD_VAR];
lKids += mAges[DEAD - CHILD_VAR] - mAges[DEAD];
lKids /= CHILD_VAR;
// Compare to average for this population; get a second crowding
// factor that will even out yearly reproduction
double lKidCrowd = lKids * DEAD / 2;
lKidCrowd /= pop;
lKidCrowd -= 1;
// Range of adults (possible partners for reproducers)
int lAdRange = mAges[MATURE] - mAges[DEAD];
// Where to start putting kids in the array
int lKid = mAges[0];
// Debugging stats
int lKidCountGood = 0;
int lKidCountBad = 0;
double lKidFitGood = 0;
double lKidFitBad = 0;
for (int age = MATURE; age < DEAD; age++)
{
int lStart = mAges[age + 1];
int lFinish = mAges[age];
int lNum = 0;
for (int i = lStart; i < lFinish; i++)
{
int aPos = i % MAX_CRIT;
int lOffset = aPos % (CHILD_VAR);
// The offset is to spread out each generation's reproduction
// as per real life and to smooth out the population.
// It is controlled by array position - cheap to calculate.
if ((age == MATURE + lOffset) ||
(age == MATURE + CHILD_GAP - lOffset))
{
lNum++;
int i2 = choice(lAdRange);
reproduce(lKid % MAX_CRIT, i % MAX_CRIT, i2 % MAX_CRIT);
double lFit = fitness(lKid % MAX_CRIT) - lCrowding - lKidCrowd;
double lFitChance = pow(mFitness, lFit);
// No exceeding Haldane limit!
if (lFitChance > mInc)
{
lFitChance = mInc;
}
// Chance this kid survives:
lFitChance /= CHILDREN;
if (lFitChance >= survivalChance())
{
lKid++;
lKidFitGood += lFitChance;
lKidCountGood++;
}
else
{
lKidFitBad += lFitChance;
lKidCountBad++;
}
}
}
}
// We keep increasing our indices and getting back inside
// the array with mod operations; but there's a limit to that.
int lShift = 0;
if (mAges[DEAD] > MAX_CRIT)
{
lShift = MAX_CRIT;
}
// Crowding should stop the population getting this big.
if (mAges[0] - mAges[DEAD] > MAX_CRIT)
{
cout << "Population exceeded limits!!!" << endl;
for (int i = 0; i < DEAD + 1; i++)
{
cout << "Array " << i << " is " << mAges[i] << endl;
}
cout << "Fitsum: " << mFitSum << " Count: " << mFitCount <<
" Avg: " << (mFitSum / mFitCount) << endl;
assert(mAges[0] - mAges[DEAD] <= MAX_CRIT);
}
// Everyone shifts up an age, and the oldest fall off the end.
for (int i = DEAD; i > 0; i--)
{
mAges[i] = mAges[i-1] - lShift;
}
mAges[0] = lKid - lShift;
mYears++;
}
// Print stats and see if we should stop yet
bool DNAStore::analyze()
{
bool bStop = false;
cout << ".";
int maxval[TRAITS];
for (int i = 0; i < TRAITS; i++)
{
maxval[i] = 0;
}
int pop = mAges[0] - mAges[DEAD];
int lBase = mAges[DEAD];
for (int t = 0; t < mTraits; t++)
{
int tc = 0;
for (int i = 0; i < pop; i++)
{
int cr = (i + lBase) % MAX_CRIT;
int loc = MAX(mDNA[cr][0][t], mDNA[cr][1][t]);
tc += mDNA[cr][0][t] + mDNA[cr][1][t];
maxval[t] = MAX(maxval[t], loc);
}
// Avg critter has a beneficial mutation in this trait
if (tc >= pop * 2)
{
// Mutation substituted!
// Start a new one!
for (int i = 0; i < MAX_CRIT; i++)
{
mDNA[i][0][t] -= 1;
mDNA[i][1][t] -= 1;
}
maxval[t] -= 1;
mMutations++;
cout << "!" << mMutations;
if (mMutations >= mMaxMutations)
{
bStop = true;
}
}
}
// Every generation some newborn gets a beneficial mutation
int lYoung = mAges[0] - mAges[1];
// Complain if there are no newborns - something's wrong!
if (lYoung <= 0)
{
cout << "0:" << mAges[0] << " 1:" << mAges[1] <<
" d:" << lYoung << endl;
for (int i = 0; i < DEAD + 1; i++)
{
cout << i << ":" << mAges[i] << " ";
}
cout << endl << "Gap: ";
for (int i = 1; i < DEAD + 1; i++)
{
cout << i << ":" << (mAges[i-1] - mAges[i]) << " ";
}
cout << endl;
assert(lYoung > 0);
}
// Beneficial mutation
for (int j = 0; j < mGoodMutations; j++)
{
int lChoose = (choice(lYoung) + mAges[1]) % MAX_CRIT;
int lWhat = choice(mTraits * 2);
mDNA[lChoose][lWhat % 2][lWhat / 2] += 1;
}
// Detrimental mutation is much more common
for (int j = 0; j < mBadMutations; j++)
{
int lChoose = (choice(lYoung) + mAges[1]) % MAX_CRIT;
int lWhat = choice(mTraits * 2);
mDNA[lChoose][lWhat % 2][lWhat / 2] -= 1;
}
cout.flush();
return bStop;
}
// Check dates and times; pause if necessary
// Return true for pause, false for keep going.
bool DNAStore::pauseExecution()
{
time_t lNow = time(0);
tm* lTime = localtime(&lNow);
bool lAfterHours = ((lTime->tm_wday == 5) || (lTime->tm_wday == 6) ||
(lTime->tm_hour <= 7) || (lTime->tm_hour >= 20));
if ((mLastSave != lTime->tm_yday) && (lTime->tm_hour >= 8))
{
// Save a file at the end of each session
ofstream fsave( "big.out", ios::trunc | ios::out );
fsave << *this;
mLastSave = lTime->tm_yday;
}
if (!lAfterHours)
{
ifstream ifcheck( ".after_hours", ios::in );
if ( !ifcheck.fail() )
{
lAfterHours = true;
}
ifcheck.close();
}
// After hours is when we run.
if (!lAfterHours)
{
// Now sleep until the next time check
sleep(10);
}
return !lAfterHours;
}
// Run a trial for this setup
void DNAStore::runTrial()
{
time_t lStart = time(0);
cout << "Increase: " << mInc << " Fitness: " << mFitness <<
" Gen: " << GEN << " Pop: " << CRITTERS <<
" Good: " << mGoodMutations << " Bad: " << mBadMutations <<
" Traits: " << mTraits << " Expect: " << mMaxMutations << endl;
bool bStop = false;
while (!bStop)
{
if (!pauseExecution())
{
nextYear();
if (mYears % GEN == 0)
{
bStop = analyze();
}
if (mYears % 10000 == 0)
{
cout << "@" << (mYears / GEN);
}
if ((mYears / GEN) >= 500000)
{
bStop = true;
}
}
}
int lGen = mYears / GEN;
time_t lFinish = time(0);
double duration = lFinish - lStart;
cout << endl << "Time taken " << duration <<
"(sec) Per generation: " << (duration / lGen) << endl;
cout << "Increase: " << mInc << " Fitness: " << mFitness <<
" Gen: " << GEN << " Pop: " << CRITTERS <<
" Good: " << mGoodMutations <<
" Bad: " << mBadMutations << endl;
cout << "Generations: " << lGen << " Mutations: " << mMutations <<
" Rate:" << ((lGen + mMutations - 1) / mMutations) <<
" Traits: " << mTraits << endl;
ofstream report( "result.out", ios::out | ios::app);
report << "Increase: " << mInc << " Fitness: " << mFitness <<
" Gen: " << GEN << " Pop: " << CRITTERS <<
" Good: " << mGoodMutations <<
" Bad: " << mBadMutations << endl;
report << "Generations: " << lGen << " Mutations: " << mMutations <<
" Rate:" << ((lGen + mMutations - 1) / mMutations) <<
" Traits: " << mTraits << endl;
}
void DNAStore::summary()
{
cout << "Increment:" << mInc << endl;
cout << "Fitness:" << mFitness << endl;
cout << "Good Mutations:" << mGoodMutations << endl;
cout << "Bad Mutations:" << mBadMutations << endl;
cout << "Years:" << mYears << endl;
cout << "Traits:" << mTraits << endl;
cout << "Mutations:" << mMutations << endl;
cout << "MaxMutations:" << mMaxMutations << endl;
cout << "Age Markers: ";
for (int i = 0; i < DEAD + 1; i++)
{
cout << mAges[i] << " ";
}
cout << endl;
}
// Print some mutation info
void DNAStore::printStats()
{
summary();
int count[260];
cout << "Increase: " << mInc << " Fitness: " << mFitness <<
" Gen: " << GEN << " Pop: " << CRITTERS <<
" Good: " << mGoodMutations << " Bad: " << mBadMutations <<
" Traits: " << mTraits << " Expect: " << mMaxMutations << endl;
cout << "Pop size limits:" << mAges[0] <<
" and " << mAges[DEAD] << ", total " <<
(mAges[0] - mAges[DEAD])<< endl;
cout << "Counting DNA types" << endl;
for (int i = 0; i < 260; i++)
{
count[i] = 0;
}
int lS = mAges[DEAD];
int lF = mAges[0];
for (int p = lS; p < lF; p++)
{
int i = p % MAX_CRIT;
if (p % 10000 == 0)
{
cout << ".";
}
for (int j = 0; j < COPIES; j++)
{
for (int k = 0; k < TRAITS; k++)
{
int index = mDNA[i][j][k] + 128;
count[index]++;
}
}
}
cout << endl;
int sum = 0;
for (int i = 0; i < 260; i++)
{
if (count[i] != 0)
{
cout << "(" << (i-128) << ") " << count[i] << endl;
sum += count[i];
}
}
cout << "Sum is " << sum << " expected " <<
((lF - lS) * COPIES * TRAITS) << endl;
cout << "Population is " << (mAges[0] - mAges[DEAD]) << endl;
}
MAIN.CPP
#include <fstream>
#include <iostream>
#include <stdlib.h>
#include "DNASTORE.H"
#include <time.h>
#include <assert.h>
void runTrial(DNAStore* aWorld, int aTraits, int aCount,
int aGood, double aInc, double aFitness)
{
// Don't allow excessive bad mutations, but require at least
// one to match each beneficial.
// Each generation is about CRITTERS / 20; max out the harmful
// mutations at 1 in 50.
int bad = DNAStore::CRITTERS / 1000;
if (bad < aGood)
{
bad = aGood;
}
aWorld->reset(aTraits, aCount, aGood, bad, aInc, aFitness);
aWorld->runTrial();
}
void runTrials(DNAStore* aWorld, int aGood, double aInc, double aFitness)
{
runTrial(aWorld, 1, 10, aGood, aInc, aFitness);
runTrial(aWorld, 3, 30, aGood, aInc, aFitness);
runTrial(aWorld, 7, 70, aGood, aInc, aFitness);
runTrial(aWorld, 14, 140, aGood, aInc, aFitness);
runTrial(aWorld, 23, 300, aGood, aInc, aFitness);
}
// Not really beautiful yet; if you have to stop half way through a
// run, only the trial you were running gets restarted.
// If that happens, you need to rewrite and recompile.
int main()
{
srand( (unsigned)time( NULL ) );
DNAStore* world = new DNAStore();
ifstream ifsave( "big.out", ios::in );
if ( !ifsave.fail() )
{
ifsave >> *world;
world->printStats();
world->runTrial();
}
else
{
runTrials(world, 1, 2, 2);
runTrials(world, 1, 2, 1.1);
runTrials(world, 1, 1.1, 2);
runTrials(world, 1, 1.1, 1.1);
}
return 0;
}
Friday, June 06, 2008
Haldane's Dilemma for Humans
In this scenario, each creature again has a collection of paired chromosomes. This time, each chromosome can have positive and negative mutations; each mutation still has the same weight, so a chromosome is summed up as a net count of mutations. Once again each child inherits one of each chromosome pair from each parent. The beneficial mutations are dominant; so if one chromosome has a net of +1 (one beneficial mutation), and one of -1 (one detrimental mutation, or an absence of a previously substituted beneficial mutation), then the child's phenotype is of +1.
Each parent has two children of their own, selecting one of the other adults at random to be the other parent of each child (so on average each individual is parent to four children - their own two and two others). Instead of culling out exactly the weakest members of each generation, each child has a chance of surviving based on the value of its mutations. The base chance is 0.5 (since there are two children per parent), times the mutation fitness (to an appropriate power, positive or negative), divided by a population-dependent factor (chosen so that a population with one beneficial mutation on every chromosome would increase until it was double the base population), and then limited to the "Haldane limit".
The tricky part was deciding when to introduce new mutations, since I wanted to see how long it took to substitute many mutations over a long time. With individual selection, the last few instances of the old type can last as recessives for a while, and occasionally survive to adulthood through luck, despite being inferior. I soon realized that in fact this isn't how it works in reality; mutations just turn up at random times. So I did that.
Each generation, one beneficial mutation is given to a random child, in a random copy of a random chromosome. This may simply remove a previous detrimental mutation, or add another copy of a beneficial mutation that's already halfway to substitution (effectively wasting it). It could also add a new beneficial mutation on a chromosome that was only partway through substitution, doubling the payoff for substituting it. At the same time, a number of detrimental mutations (I used 1000) are added the same random way. One of them could potentially eliminate the one we just added.
The results are more random than the truncation selection example; the competition between mutations is much more random, and it's easy for older alleles to hang around for quite a while as recessives. Also, there are a number of ways that a beneficial mutation can be wasted:
- it can "drift" out of the population, through bad luck in selection or simply by not being passed on to descendants in the genetic mix. This is much more likely for low-benefit mutations, or a low Haldane limit, or early in the selection process.
- it can appear on a chromosome that already has a detrimental mutation on it, cancelling the effect. Equivalently, a detrimental mutation can turn up on its chromosome afterwards.
- it can appear on a chromosome which is in the middle of a substitution. In a population of a million, if half the individuals already have mutation A, then another copy of mutation A even for an individual that already possessed it will make no difference at all to how fast the mutation substitutes. A brand new mutation B (with the same selective strength as A) on a chromosome that doesn't possess A will appear from a selective point of view to be another copy of A, and unless some genetic mixing occurs it will be a neutral mutation once A finishes substituting.
However, there is also a way for a beneficial mutation to get a free ride. If mutation A is already in the process of substituting, and mutation B appears on a chromosome that already has A, then B gets a free ride towards substitution. The smaller the population of A at the time, the lower the chance of it happening, but the bigger the free ride.
These last two effects cancel each other out - if the current population of a beneficial mutation is f out of an overall population of K, then the free ride is worth K/f, but the chances of getting it is f/K. The chances of wasting the mutation is (K - f) / K, and the benefit is 0. Total: f/K x K/f + (K - f)/K = 1.
What that means is that the more beneficial mutations appear, the more will be selected. On the other hand, more detrimental mutations will slow down the substitution rate. No big surprises there.
So what are the results? I took a population of one million (Remine's example size) and up to 23 chromosomes (human size); 1000 negative mutations for each positive one, and a variety of fitness and reproductive excess. At fitness and reproductive excess of 1.1, and one chromosome, we expect that Remine's predictions will hold. And indeed we see 163 generations per substitution - more than the theoretical limit of 138, less than the estimate of 300. With three chromosomes, that drops to 72 generations per substitution - well under Remine's lower limit. With seven chromosomes, it's down to 42 generations; with 14, it falls to 33; and at 23 (as for humans) it's 27 generations. That is, eleven times faster than Remine expects.
For completeness, I ran a simulation over 500,000 generations - Remine's grand example - with a selection coefficient and a mutation benefit of 1.1, using 23 chromosomes per individual. That should tell us how many beneficial mutations could be substituted jointly by humans and chimps over the 5 million years since our common ancestor. The answer? 20788 beneficial mutations, at a rate of one every 25 generations. For comparison, humans and chimps both have about 22,000 genes; a recent survey of about 2/3 of the chimp/human genome (http://www.pnas.org/cgi/reprint/0701705104v1) showed 387 genes that had been selected on one side or the other. So the number of actual coding differences between them is probably around 580. The Haldane limit allows a difference in almost every single gene we have, and 35 times as many gene substitutions as we need to make a human out of a chimp.
With a fitness and excess of 2 (that is, mutations that double your survival chances), the numbers are far lower. At 1 chromosome, 23 generations - which is slightly more than log2(1000,000), as you'd expect. With three chromosomes, that falls to 11; with seven, it's 8 generations; with 14 chromosomes, 6 generations; and with 23, it's 5. Fast reproducing humans with very beneficial mutations could substitute an enormous number of mutations in 5 million years... about 50,000 with a 20-year generation. Nor is a reproductive excess of 2 unreasonable - it means four children surviving to adulthood in each family. My own ancestry is full of examples.
One interesting point is that we can have a Haldane limit different to the mutation fitness. An increase of 10% per generation can be tested with mutations that change fitness by only 1%, for example. The results here show that while it is always better to have a higher Haldane limit or a higher fitness, the substitution rate is controlled by the most limiting factor. Even then, the improvement is mostly in getting closer to the theoretical maximum for that factor. So even extremely beneficial mutations do not substitute faster than whatever excess reproduction rate they engender; and even a high excess reproduction rate won't speed up the substitution of minor mutations very much.
Increasing the number of beneficial mutations also has a limited effect - adding ten mutations per generation instead of one can roughly double the substitution rate, but still won't get you over the theoretical limit. The minor exception here is that if your reproductive excess doesn't match your mutation fitness, a high beneficial mutation rate can get you over the theoretical limit for whichever one is limiting you - so for example a 1% excess with 10% mutations would normally be limited to 100 generations per substitution; but with 5 mutations per generation, it can get down to 64.
You can see the program I used to test this in a separate post; it's in C this time, as it uses very large amounts of memory and I needed to control that to make it reasonable fast. Even so, the big 500,000 generation simulation took several weeks on my desktop. Read it; run it yourself; satisfy yourself that it does what it's supposed to.
Bottom line: the dilemma that Remine presents is his alone. It's not a limit that prevents humans (or anything else) from evolving at the rates we observe.
Friday, May 30, 2008
King George
In fact, they argue that they can do this to anyone in wartime; and that this is wartime:
The full appeals court is reviewing that decision and a ruling is expected soon. During arguments last year, government lawyers said the courts should give great deference to the president when the nation is at war.
"What you assert is the power of the military to seize a person in the United States, including an American citizen, on suspicion of being an enemy combatant?" Judge William B. Traxler asked.
"Yes, your honor," Justice Department lawyer Gregory Garre replied.
So apparently we've been at war since at least late 2001, and we still are. Who wants to guess when we would be declared "no longer at war"? And when was last time we weren't? There have been terrorist attacks on US targets many times (USS Cole, embassy bombings...) and we've had soldiers fighting or peacekeeping in other countries for a large fraction of the last century.
If this man is guilty, he should be tried and sentenced, and deported. If he's not guilty, he should be released. If they "know" he's guilty, and can't prove it... that's a risk that we as a society have chosen to take and woven into the fabric of our justice system. We have chosen not to lock people up who might be innocent, even though some of them are inevitably guilty.
I don't know if they can do this or not. I'm not a constitutional lawyer. But this sort of thing is the bedrock on which the US declared independence. Two of the complaints against the original King George were:
He has affected to render the Military independent of and superior to the Civil Power.
...depriving us in many cases, of the benefit of Trial by Jury:
One of Ed's commenters asks:
Just for the sake of argument (because I don't think anyone here will argue that the government's assertion is staggering), let's say that the Judicial branch rolls over on this issue. How could one then differentiate between the USA and a typical police state?
I have heard people call for George Bush's impeachment many times in the past few years, and I have always thought this hyperbole. Sure he's incompetent and destructive in his foreign and economic policy; but that's just my opinion, and the arguments rage. This changes my mind - this sort of attack on the very fabric of the United States cannot be ignored.
Everyone from Gregory Garre on up to whatever level approved this line of argument should be sacked immediately as not fit to hold public office. If that isn't the president, then the rest of the supervisory chain should be sacked for their manifest failure to oversee operations. This is not just one comment in a courtroom; this case has been going on since 2001.
It is not enough to defend society against terrorism; we must also keep a society worth defending. The United States threw out one King George; it does not need another.
Tuesday, May 27, 2008
Coding Haldane's Dilemma
// Here's the code; I've been lazy, so you have to change constants and
// recompile it to alter incremental fitness (INC)
//
// This code is public domain. Enjoy.
import java.util.Random;
import java.util.Arrays;
import java.util.Comparator;
class Substitute
{
/** How many critters to have **/
private static final int CRITTERS = 1024;
/** How many children per critter **/
private static final int CHILDREN = 2;
/** How many selectable traits to have **/
private static final int TRAITS = 20;
/** How many copies of gene in the DNA **/
private static final int COPIES = 2;
// Note that you can't just change COPIES and expect
// everything to work!
/** Rate of increase, must be less than CHILDREN **/
private static final double INC = 1.1;
/** How may trials to run **/
private static final int TRIALS = 100;
/** A random number generator for initial state **/
private Random mRand = new Random(System.currentTimeMillis());
/** How many generations have gone by **/
private int mGenerations = 0;
class Critter
{
/** No-args constructor **/
Critter()
{
for (int i = 0; i < TRAITS; i++)
{
mDNA[0][i] = mDNA[1][i] = false;
}
}
/** Constructor from two parents **/
Critter(Critter aP1, Critter aP2)
{
for (int i = 0; i < TRAITS; i++)
{
// Choose one chromosome randomly from each parent.
int c1 = mRand.nextInt(COPIES);
int c2 = mRand.nextInt(COPIES);
mDNA[0][i] = aP1.mDNA[c1][i];
mDNA[1][i] = aP2.mDNA[c2][i];
}
}
/** Give yourself a beneficial mutation **/
void setDNA(int aPos)
{
mDNA[0][aPos] = true;
}
/** Do you have this mutation? **/
boolean getDNA(int aPos)
{
return (mDNA[0][aPos] || mDNA[1][aPos]);
}
/** DNA of critter **/
private final boolean mDNA[][] =
new boolean[COPIES][TRAITS];
};
/** Sort version that has mutations strictly ordered **/
public static Comparator LOWEST_FIRST = new Comparator()
{
/** Compare two Critters **/
public int compare(Object aOne, Object aTwo)
{
Critter l1 = (Critter)aOne;
Critter l2 = (Critter)aTwo;
for (int i = 0; i < TRAITS; i++)
{
boolean mine = l1.mDNA[0][i] | l1.mDNA[1][i];
boolean theirs = l2.mDNA[0][i] | l2.mDNA[1][i];
if (mine != theirs)
{
// Smaller is better
return mine ? -1 : 1;
}
}
return 0;
}
/** Not using this, don't care **/
public boolean equals(Object aComp) { return false; }
};
/** Sort version that has mutations equivalent **/
public static Comparator ALL_SAME = new Comparator()
{
/** Compare two Critters **/
public int compare(Object aOne, Object aTwo)
{
Critter l1 = (Critter)aOne;
Critter l2 = (Critter)aTwo;
int total = 0;
for (int i = 0; i < TRAITS; i++)
{
boolean mine = l1.mDNA[0][i] | l1.mDNA[1][i];
boolean theirs = l2.mDNA[0][i] | l2.mDNA[1][i];
if (mine != theirs)
{
// Smaller is better
total += mine ? -1 : 1;
}
}
return total;
}
/** Not using this, don't care **/
public boolean equals(Object aComp) { return false; }
};
/** DNA of current generation **/
private final Critter mCurrent[] = new Critter[CRITTERS];
/** DNA of new generation **/
private final Critter mNext[] =
new Critter[CRITTERS * CHILDREN];
public Substitute()
{
// Create the critters
for (int i = 0; i < CRITTERS; i++)
{
mCurrent[i] = new Critter();
}
// One beneficial mutation in each trait to start;
// each one in a different critter.
for (int i = 0; i < TRAITS; i++)
{
mCurrent[i].setDNA(i);
}
}
public int getGenerations() { return mGenerations; }
public void nextGeneration()
{
int remains = CRITTERS;
int lWhere = 0;
for (int i = 0; i < CRITTERS / 2; i++)
{
// Choose two random current critters
Critter p1 = getRandom(mCurrent, remains--);
Critter p2 = getRandom(mCurrent, remains--);
// Reproduce them.
for (int j = 0; j < CHILDREN * 2; j++)
{
mNext[lWhere++] = new Critter(p1, p2);
}
}
// Kill off random children to simulate how
// difficult life is for everyone.
remains = CRITTERS * CHILDREN;
long target = Math.round(INC * CRITTERS);
while (remains > target)
{
getRandom(mNext, remains--);
}
// Pick out the genetically fittest.
// Change LOWEST_FIRST to ALL_SAME to give equal
// weight to all mutations
Arrays.sort(mNext, 0, remains, LOWEST_FIRST);
// And replace the current generation with the best
for (int i = 0; i < CRITTERS; i++)
{
mCurrent[i] = mNext[i];
}
mGenerations++;
}
/** Print stats and see if we should stop yet */
public boolean analyze()
{
boolean bStop = true;
for (int i = 0; i < TRAITS; i++)
{
int tc = 0;
for (int j = 0; j < CRITTERS; j++)
{
if (mCurrent[j].getDNA(i))
{
tc++;
}
}
if (tc < CRITTERS)
{
bStop = false;
}
if (tc == 0)
{
// Lost a mutation! Replacing...
int pos = mRand.nextInt(CRITTERS);
mCurrent[pos].setDNA(i);
}
}
return bStop;
}
/**
* Pick a random critter from the array, and
* shrink the array down so it's removed.
*/
private Critter getRandom(Critter[] aGroup, int aCount)
{
int c1 = mRand.nextInt(aCount);
Critter p1 = aGroup[c1];
aCount--;
aGroup[c1] = aGroup[aCount];
return p1;
}
/**
* Main method, contructs the scenario and sets it off
* @param aArgs any command line arguments (ignored at present)
**/
public static void main(String aArgs[])
{
int lMin = 100000;
int lMax = 0;
int lTotal = 0;
for (int i = 0; i < TRIALS; i++)
{
Substitute sub = new Substitute();
sub.analyze();
boolean bStop = false;
while (!bStop)
{
sub.nextGeneration();
bStop = sub.analyze();
}
int lGen = sub.getGenerations();
if (lGen < lMin) { lMin = lGen;}
if (lGen > lMax) { lMax = lGen;}
lTotal += lGen;
System.out.println("Completed run: " + i + " Result: " + lGen);
}
System.out.println("Min: " + lMin + " Max: " + lMax +
" Average: " + ((lTotal + TRIALS/2)/ TRIALS) +
" (Trials: " + TRIALS + ")");
}
}
Simulating Haldane's Dilemma
Remine's web-page at http://saintpaulscience.com/Haldane.htm notes that "Cost arguments can apply to any evolutionary scenario, for any species, in any environment, and even including computer simulations." So results of a computer simulation (given the right assumptions) should be a valid test.
The simulation I set up worked like this: each individual has two sets of chromosomes. Each chromosome can either contain or not contain a beneficial mutation. At reproduction time, two individuals contribute one of each of their pairs of chromosomes (not necessarily all from the same set) to each child they have. The species is monogamous, so each parent mates with only one other parent, having a total of four children (2 per individual). The population starts with one of each mutation.
That would double the next generation's population, so some must be eliminated. First we kill off a randomly chosen set of individuals, to bring the numbers back to 1.1 per parent. This simulates accidents, harmful mutations, random predation, disease, and all the other things that interfere with selection. Now we take the best of the remaining individuals and make that the next generation.
Selection can be done in two ways. We can count the number of beneficial mutations each individual possesses, and sort by that. This assumes that each is about equal, and the more you have the better. Or we can sort "alphabetically" - that is, the first mutation rules unless both individuals have it; try the second if the first was even; then try the third, etc. This assumes that some mutations are better than others - the mutations do not interact especially favorably, but are still always advantageous.
The results of the simulation are naturally going to be variable; particularly in the early stages of a substitution, it's easy for a beneficial mutation to drift out when its numbers are small. The simulation assumes that beneficial mutations are available; if any vanish, they are replaced with a new single instance.
The result? I had a population size of 1024, so Remine and I would both agree that selecting one mutation takes about 80 generations (log2048 / log1.1 - it's 2048 because there are two copies of each gene). Where we differ is in how long it takes to substitute more. He would claim 80 generations for each extra substitution; I would claim only 11, and some of that to take place during the initial 80 generations.
With 1 mutation to substitute, 100 trials gave results anywhere between 53 and 192 generations, at an average of 86. That matches the prediction reasonably well (because of the random heritability, a mutation may leave anywhere from 0-4 copies, so sometimes a mutation will grow faster than expected or even shrink, particularly early in the run). With 5 mutations, Remine would predict 80 generations each - a total of 400. I would predict 80 for the first one, and less than 11 each for the rest (total of 124), as some of the substitution would be done in parallel.
Results of 100 trials: 92-243, at an average of 130.
With 20 traits, I predict 80 + 19 * 11 = 289; Remine predicts 1600.Results of 100 trials: 238-366, at an average of 279. Note that as larger numbers are involved, the results are more predictable.
Above I mentioned two different selection types; what happens if these are tried? The alphabetical approach means the first mutation always substitutes first, and so on in order. The counting approach means all the mutations end up substituting about the same time. But there is no significant difference in the overall approach (20 traits over 100 trials, 236-361, average 277).
Remine complains that low selection coefficients are generally assumed, which lowers the cost. So let's try 2 as the growth rate instead of 1.1:
1 Trait - both of us predict about 11 generations.
100 trials: 9-14, at an average of 11.
5 Traits - I predict about 11 + 4*2 = 19, Remine predicts 5*11 = 55
100 trials: 18-24, at an average of 20
20 Traits - I predict about 11 + 19*2 = 49, Remine predicts 20*11 = 220
100 trials: 54-65, at an average of 57
As you can see, Remine's expectations are not met.
Remine does make some valid points about the assumptions made; only some mutations will substitute this fast. But if you look at a genome, it will typically be dominated by the fast-substituting mutations, because more of them can substitute in a given time. Nor is it much of a surprise that the first species that's capable of figuring this out would come at the end of a burst of faster-than-average mutation; any new capability such as provided by human intelligence opens up a wide field of potentially beneficial mutation.
But the computer simulation confirms what the mathematics shows - the 300 generation speed-limit that Remine talks about is far slower than what is possible in reality. His argument fails to stand up, at least here.
This assumes truncation selection, which Remine doesn't like. So my next effort was to try individual selection, to see if that made a difference. Once again, this post is long enough already, so I'll leave that for another.
(Incidentally, if you're curious to see what the simulation looks like, I've added that in a separate post).
Friday, May 16, 2008
Haldane's Dilemma
The essence of the idea is that there is a limit on how fast beneficial mutations can be substituted into the genome by natural selection, and that limit is too slow to add meaningful change to the human genome since the split from the chimp/human common ancestor.
A crucial part of this is that substituting multiple mutations at once does not speed this up; if one mutation takes n generations, 2 mutations takes 2n generations. This conclusion is far as I can tell based purely on Haldane's work, which again as far as I can tell comes down to this paragraph:
"Can this slowness be avoided by selecting several genes at a time? I doubt it, for the following reason. Consider clonally reproducing bacteria, in which a number of disadvantageous genes are present, kept in being by mutation, each with frequencies of the order of 10^-4. They become slightly advantageous through a change of environment or residual genotype. Among 10^12 bacteria there might be one which possessed three such mutants. But since the cost of selection is proportional to the negative logarithm of the initial frequency the mean cost of selecting its descendants would be the same as that of selection for the three mutants in series, though the process might be quicker. The same argument applies to mutants linked by an inversion. Once several favorable mutants are so linked the inversion may be quickly selected. But the rarity of inversions containing several rare and favorable mutants will leave the cost unaltered."
Many of you will have already spotted the key words here - "clonally reproducing". That means that if you don't have the beneficial allele in your DNA already, the only way your descendants will get it is by mutation. So although each mutation may be present at 10^-4, there is no way to combine them unless they randomly already are combined. The mutations must occur in a single line of descent.
But humans don't reproduce that way (in reality, neither do bacteria). Humans reproduce sexually, which allows beneficial mutations to spread individually and combine later. So how does this change Remine's numbers?
Essentially Remine's formula (and Haldane's) is that the minimum number of generations required for substitution is the log of the population size (in his paper he uses a population of 1,000,000) divided by the selection intensity (which can be roughly considered as the log of the maximum growth rate, and which Haldane estimates at 0.1 for typical species).
So, log (1,000,000) / 0.1 = 13.8 / 0.1 = 138 generations per mutation; Haldane considered that 300 generations would be a more typical figure. This represents a beneficial mutation starting out in one individual, and growing at the expense of the old-type genome, until it completely replaces it.
The beneficial mutation to be substituted will be found on one of the chromosomes in the genome. In a sexually reproducing species, you would expect the substitution to leave the variation in the other chromosomes untouched, as they would be thoroughly mixed by reproduction. Variation in the substituting chromosome would depend on the rate at which chromosomes recombine. If no other beneficial mutations were around, then the entire 138 generations would indeed be required for a single substitution.
What if there are other beneficial mutations, however? They would have considerable room to increase their own frequencies, just as the target mutation does. A mutation that grows exponentially from 1 to 1M will spend half its growth time at a frequency of less than 1 in 1000 - basically invisible to other mutations. So 1,000 other mutations might easily do half their substitution on other chromosomes, stay around unchanged as the second half of the substitution took place, and then fight it out to see who went next. Two substitutions in only 1.5 times the minimum for one.
Let's formalize this intuition by considering a population of constant size K, with a maximum growth rate for beneficial mutations of r (where selection intensity = log r). It would take n generations to substitute, where K = r^n (r is the nth root of K). So in generation i, there will be r^i individuals with a copy of the gene (completely removing all the old allele is roughly equivalent in cost to substituting into a population of 2K, which we'll ignore for now). And this will be the result of (r-1)r^i deaths of old-type individuals - and the extra births of the same number of new-type individuals.
So we expect that in each generation, (K-r^i) individuals don't have the new gene, but aren't killed off to substitute it, and are available as a population for the substitution of other genes. So our new gene is using up r^i/K of the species' ability to substitute new genes, in that generation.
Over the course of the entire substitution, it will use up 1/K(1 + r + r^2 + ... + r^n). Using the old compound interest formula, the total cost usage is then 1/K(r^(n+1) - 1)/(r - 1). Remembering that r^n = K, and assuming K >> 1, that simplifies to r/(r - 1), which for r = 1.1 means one substitution every 11 generations.
The limit described by Haldane is the latency, not the throughput. It will still take 138+ generations to subsitute a given mutation into a population of 1M. But others could start or finish during that time, using the leftover capacity described above.
Note that the throughput result, unlike the latency, is no longer dependent on the population size. A bigger population takes longer to subsitute individual mutations, but can be working on more things simultaneously (and actually has more capacity to generate benefical mutations). The only thing that matters is how much excess reproductive capacity the species has.
Sidenote: Remine spends most of a chapter of "The Biotic Message" wondering how evolution can explain the origin of sexual reproduction. Above we see that throughput of beneficial mutations is one every logK/logr generations for asexual species, and roughly one every 1/logr generations for sexual species. That is, sexual reproducers can adapt to new situations about logK times faster. This is *more* pronounced for large populations such as bacteria - and no wonder they still use sex, even if they don't tie it so directly to reproduction. Any multicellular species that failed to allow sex somewhere in its reproductive cycle would have been outcompeted by its rivals in a geological heartbeat.
How does this apply to humans? Remine claims that over the last 10M years, humans could have only substituted 500,000 / 300 = 1,667 mutations. The above shows that we are in fact capable of up to 500,000 / 11 = 45,454! And it's worth noting that the human race is currently doubling in size about every 50 years, even with very low birth rates in the developed world, and all the wars, famine, disease etc that we see. That would give a figure for r of 1.32 (assuming generations of 20 years), or one substitution every 4.125 generations. And that rate requires 2.64 children per family surviving to adulthood; clearly plausible.
The latest results from the human genome project (see http://www.ensembl.org/Homo_sapiens/index.html) show that humans have 21,541 genes. If we split from chimps 5M years ago - 250,000 generations - we've had time to substitute 60K mutations at 4.125 generations each. That is, almost three important mutations per gene; and that doesn't include neutral differences like multiple ways of coding for the same amino acid, or amino acid changes that don't affect protein function, all of which can piggyback on beneficial mutations. And that's just our side; the chimps have had time to do the same thing on theirs.
Calculations like the above are fine things, but experiment trumps theory. Fortunately, Remine notes that these limits also apply to computer simulations - so I don't need to keep a few hundred generations of cows at home to test my results. But this post is already way too long, so I'll leave that for another post.