An Algorithmic Lucidity

a blog

August 2013

Online Dating Profile, First Draft

25 / M / Straight / Single Walnut Creek, California

My self-summary

It hardly seems fair to ask for a self-summary—how could the rich tapestry of a human mind (with all its hopes, fears, memories, anticipations, and quirks) possibly be summarized in a mere paragraph? Or maybe that's a cop-out, a facile rationalization offered not because it has any chance of being believed, but because the real truth is too terrible to face: maybe the real reason one finds oneself reluctant to write a self-summary is not that there's too much to say (and therefore, that to say anything in particular would leave out too much and be a contemptible misrepresentation), but precisely that one fears there is too little: if we actually knew how much of our behavior could be predicted by simple programs informed by broad demographic data, a few personality parameters, and a small correction term for subcultural memetic noise, would our self-esteem survive the blow? What does it mean to be human in an inhuman universe, to hold true to one's ideals even after formulating the now-dominant hypothesis that those ideals were just folderol evolved to cover up the unavoidable responsibility of action in a world of crazy talking monkeys?

I don't know the answers. But I want to work to find them out. To face them, together, with you.

What I'm doing with my life

(Written 30 August 2013.) This autumn, I shall be attending App Academy's web development course, after which I shall seek employment as a programmer.

I'm really good at

starry-eyed idealism, reasoning under uncertainty

The first things people usually notice about me

My ... hair? My desperate and ineffectual attempts to signal how "smart" and "creative" I am? But probably my hair.

Favorite books, movies, shows, music and food

Text: collected works of Greg Egan and Eliezer Yudkowsky, Atlas Shrugged (it's not what it looks like, I swear)
Television: My Little Pony: Friendship Is Magic, Star Trek (TNG, DS9, VOY)

The six things I could never do without

internet access, mechanical pencils, the knowledge that it's easier to ask forgiveness than it is to get permission, em dashes, the magic of friendship, the global economy, being forgiven for listing seven things when I was only given permission for six

I spend a lot of time thinking about

Consider reading my blog to get an idea of the sorts of thing I think about.

On a typical Friday night I am

doing something wholesome and virtuous

The most private thing I'm willing to admit

... pass?

I'm looking for

  • Girls who like guys
  • Ages 19–30
  • Near me
  • For new friends, long-term dating, short-term dating

You should message me if

and only if it turns out that messaging me is the right thing to do

do/end Macros for Emacs

Dear reader, Ruby is a pretty okay programming language, but I have to say I feel ambivalent about the use of do and end as block delimiters. (Contrast to braces in C/Java/&c. or indentation in Python.) Three keystrokes just to close a block?! Scandalous!

Or maybe ... not-so-scandalous. For two or three characters need not imply two or three keystrokes; one need only configure one's editor with convenient bindings for the insertion of do and end. For example, pasting the following code into one's Emacs init file assigns M-[ (respectively M-]) to insert the text do (respectively end), much as one would type Shift-[ (respectively Shift-]) for an open- (respectively close-) brace, except with Alt ("Meta" in Emacs parlance) instead of Shift—

(fset 'block-do
   "do")
(global-set-key (kbd "M-[") 'block-do)

(fset 'block-end
   "end")
(global-set-key (kbd "M-]") 'block-end)

I guess this would also be useful for like, Lua.

I'm a Moron

It's always tempting to make excuses for our past selves, to tell a story about how, despite the appearance of continual failure and waste, we were actually in the right all along.

It's not true. If we were really in the right, we wouldn't need to strain to tell a complicated story explaining why; it would just be obvious from appearances.

When I find myself tempted to tell stories, it takes a deliberate effort to remind myself to be honest, but it's important. I was a fool; I was worse than a fool, and maybe I happened to get away with it, sort of, so far, but there's so much that I should have known, should have guessed, should have realized, should have considered. Of course there's no use dwelling on it overmuch, for we cannot make decisions about the past. But that's not the same as it being okay that it happened that way. It's not.

Measure

In a sufficiently large universe, everything that can happen happens somewhere, but it's clearly not an even distribution. Flip a quantum coin a hundred times, and there have to be some versions of you who see a hundred heads, but they're so vastly outnumbered by versions of you who see a properly random-looking sequence of heads and tails that it's not worth thinking about: it mostly doesn't happen. When you write a computer program, or build a bridge, or just think something, we might prefer to take the viewpoint that you're not creating anything so much as you are instantiating that pattern locally, thereby increasing its measure in the multiverse: there might be other ways for that program, that bridge, that thought to come about somewhere, but it's getting some of its support from you. Decisionmaking is about exerting some control over the distribution of measure over patterns in the multiverse: agent-like patterns select actions so as to allocate measure to their preferred patterns. Maybe there are some versions of me with an ice-cream cone that form by chance, or are deliberately created by alien civilizations investigating how humans respond to ice-cream, but if I get an ice-cream cone, it's mostly because humans evolved and then developed cultures which domesticated dairy cows and cultivated sugar and so on and so forth until eventually I was born and grew up and put effort into acquiring ice-cream. When you decide, you help determine the distribution of what happens to the sections of the multiverse that depend on copies of your decision—choose carefully.

The Future of Ideas

William Gibson famously said, "The future is already here—it's just not very evenly distributed." It's easy to imagine a science-fictional fantasy world where everything is made of diamond and plastic, and literally everyone has their own brigade of robots, spacepacks, and jetcars to do their bidding, but as Gibson points out, the real world doesn't actually work like this: there's nothing contradictory about the high technology allowing you to read this post existing in the same world where millions of others are starving, thirsty, and illiterate. The Earth is just a very big place compared to what we know how to imagine personally; the wealth and wonders that exist in some places, don't exist everywhere. As long as this is true, we should expect variance in wealth to increase, as new toys for the rich get invented faster than the basics can be provisioned for everyone; Carlos Slim can purchase extravagances that hadn't been invented in the days of Cornelius Vanderbilt, but dying of malaria is the same as it's ever been.

A similar thing could be said about knowledge and ideas. Human civilization has been rapidly accumulating knowledge, but we're not getting proportionately more capable as individuals. People typically don't have the resources or inclination to learn deeply outside of their own specialties, and many never get to master any specialty at all. There's nothing contradictory about our brightest scholars seeing more deeply into the true structure of the world beneath the world than the uninitiated would have ever conceived possible, while at the same time, the masses labor under the most primitive of superstitions. As long as this is true, we should expect variance in knowledge to increase, as the cognitive elite continues to advance the frontier of the known faster than the basics can be taught to everyone; our master biologists know more about the nature of life than their analogues in the days of Darwin and Wallace, but to the proletariat, "God did it in six days" probably still sounds like as good of an explanation as it's ever been.

Plurals of Coinflip Outcomes

We speak of the outcome of a coinflip being heads or tails, which are surely singular nouns in this context, despite being derived from the plural forms of head and tail. So when we flip a coin twice and get heads (respectively tails) twice, should we describe the outcome as two heads (respectively tails), or two headses (respectively tailses)? I feel like you could make a logical case for the latter, but I guess the former does sound more natural?

Forgetting an Idea

Occasionally I have a good idea, but neglect to write it down immediately, and end up forgetting it very soon thereafter; often I can ressociate my way back to it, but not always. I'm given to understand that this is not uncommon for other people, either. Only I have to wonder if it's at all telling that we remember the emotional experience of "I just had a good idea! Clearly I am a Smart and Creative Person!" but forget the idea that was ostensibly its referent. Shouldn't it be the other way around? Why, it's almost as if the deception and posturing that defines our social worlds extends even into the sacred domain of the self!

Ford-Fulkerson

Dear reader, have you ever dreamed of solving instances of the maximum flow problem? Sure you have! Suppose we have a weighted directed graph, which we might imagine as a network of pipes (represented by the edges) between locations (represented by the nodes), pipes through which some sort of fluid might thereby be transported across the network. One node is designated the source, another is called the sink, and the weight of the edge (i, j) represents the maximum capacity of the pipe which transports fluid from the location i to location j. The maximum-flow problem is precisely the question of how to transport the maximum possible amount of fluid from the source to the sink (without any fluid leaking or magically appearing at any of the intermediate nodes). That is, we want to assign an amount of fluid flow to each edge, not to exceed that edge's capacity, such that inflow equals outflow for all the intermediate (i.e., non-source, non-sink) nodes, and such that the total flow reaching the sink is maximized.

It turns out that there's a conceptually straightforward algorithm for solving the maximum flow problem, known as the Ford-Fulkerson method, which we'll implement in Ruby. But first, we'll want to pin down exactly how the flow network will be represented. Let's define an Edge class, each instance of which will be initialized with the names of the nodes at its head and tail, and the edge's maximum capacity:

class Edge
  attr_accessor :tail
  attr_accessor :head
  attr_accessor :capacity

  def initialize(t, h, c)
    @tail = t
    @head = h
    @capacity = c
  end
end

And let's also make a FlowNetwork class, defined by the names of its source and sink nodes, and a hash which maps Edges to the amount of flow currently assigned to that Edge:

class FlowNetwork
  attr_accessor :source
  attr_accessor :sink
  attr_accessor :network

  def initialize(source, sink, edges)
    @source = source
    @sink = sink
    @network = {}
    edges.each do |e|
      @network[e] = 0
    end
  end

(Note that I haven't yet closed the FlowNetwork class definition yet, because we still want to define more methods implementing the Ford-Fulkerson algorithm!)

So how do we solve the maximum-flow problem? It's pretty simple! We start with the "zero flow." (In our code above, this is already done when we initialize an instance of FlowNetwork.) We then find an augmenting path from the source to the sink (possibly including edges traversed backwards) such that all the forward-traversed edges along the path have not been assigned their maximum capacity, and all the backward-traversed edges have a nonzero amount of flow. This is path along which we can push more flow from the source to the sink, by increasing the amount of flow along the forward-traversed edges, and decreasing the amount of flow along the backward-traversed edges. (Convince yourself that pushing flow through an edge backwards amounts to decreasing the amount of flow along that edge.) Once we've found such an augmenting path, we push as much flow as we can along it. Then we look for another augmenting path and do the same, until no more augmenting paths can be found. It turns out that the resulting flow assignment solves our problem. So that's the Ford-Fulkerson method:

  def ford_fulkerson
    path = augmenting_path
    while path
      flow_augmentation(path)
      path = augmenting_path
    end
  end

Of course, we need to specify exactly how to find these augmenting paths. This is a little bit more involved. It amounts to doing walking through the graph starting at the source, "labeling" nodes that could be part of an augmenting path, and scanning neighbors of labeled nodes looking for more labelable nodes, until we reach the sink (in which case we have found a path) or until we run out of nodes to scan (in which case there are no augmenting paths left). Sort of like this:

  def augmenting_path
    labeled = {@source=>nil} # keys are labeled nodes; values, parents thereof
    scanned = {}
    now_scanning = @source
    while not labeled.empty?
      if labeled.include?(@sink) # i.e., we've found an augmenting path
        backtrace = [@sink]
        parent = labeled[@sink]
        while parent != nil # reconstruct the path found
          backtrace.push(parent)
          parent = scanned[parent]
        end
        return backtrace.reverse!
      end
      edges = @network.select do |e, v|
        e.tail == now_scanning or e.head == now_scanning
      end
      edges.each do |e, v|
        if e.tail == now_scanning
          if @network[e] < e.capacity
            if not labeled.merge(scanned).include?(e.head)
              labeled[e.head] = now_scanning
            end
          end
        elsif e.head == now_scanning
          if @network[e] > 0
            if not labeled.merge(scanned).include?(e.tail)
              labeled[e.tail] = now_scanning
            end
          end
        end
      end
      scanned[now_scanning] = labeled[now_scanning]
      labeled.delete(now_scanning)
      now_scanning = labeled.keys[0]
    end
    return nil # no path found
  end

And of course, we also need code to actually augment the flow along the path found—

  def flow_augmentation(path)
    flow = +1.0/0 # positive infinity
    edges = []
    def query_edge(tail, head) # select edge given node names
      @network.select{|e, v| e.tail == tail and e.head == head}
    end
    path[0..path.length-2].each_index do |i|
      forward = query_edge(path[i], path[i+1])
      backward = query_edge(path[i+1], path[i])
      if backward.empty?
        edge_flow = forward
        edges.push(edge_flow.keys[0])
        available = edge_flow.keys[0].capacity - edge_flow.values[0]
        if flow > available
          flow = available
        end
      elsif forward.empty?
        edge_flow = backward
        edges.push(edge_flow.keys[0])
        available = edge_flow.values[0]
        if flow > available
          flow = available
        end
      end
    end
    edges.each do |e|
      network[e] += flow
    end
  end

Then throw in a reporting method so we have some way of actually inspecting our network and its completed flow assignment, close the FlowNetwork class definition (at long last) ...

  def report
    print "Source: ", @source, "\n"
    print "Sink: ", @sink, "\n"
    @network.each_pair do |e, v|
      print e.tail, ' -> ', e.head, '; capacity: ', e.capacity, ', flow: ', v, "\n"
    end
    puts
  end

end

—and dear reader, the maximum-flow problem is solved! For suppose we are faced with the network depicted in the following diagram:

our flow network

We can then (admittedly with some typing) call for a solution like so:

my_edges = [Edge.new('A', 'B', 12), Edge.new('A', 'E', 15),
  Edge.new('A', 'G', 13), Edge.new('B', 'C', 9), Edge.new('E', 'C', 11),
  Edge.new('G', 'E', 7), Edge.new('C', 'D', 18), Edge.new('C', 'F', 10),
  Edge.new('H', 'E', 8), Edge.new('G', 'H', 12), Edge.new('F', 'D', 6),
  Edge.new('H', 'F', 6), Edge.new('D', 'I', 12), Edge.new('F', 'I', 20),
  Edge.new('H', 'I', 10)]
my_network = FlowNetwork.new('A', 'I', my_edges)
my_network.ford_fulkerson
my_network.report

And receive it like so:

Source: A
Sink: I
A -> B; capacity: 12, flow: 9
A -> E; capacity: 15, flow: 11
A -> G; capacity: 13, flow: 12
B -> C; capacity: 9, flow: 9
E -> C; capacity: 11, flow: 11
G -> E; capacity: 7, flow: 0
C -> D; capacity: 18, flow: 12
C -> F; capacity: 10, flow: 8
H -> E; capacity: 8, flow: 0
G -> H; capacity: 12, flow: 12
F -> D; capacity: 6, flow: 0
H -> F; capacity: 6, flow: 2
D -> I; capacity: 12, flow: 12
F -> I; capacity: 20, flow: 10
H -> I; capacity: 10, flow: 10

Bibliography

Dimitris Bertsimas and John N. Tsitsiklis, Introduction to Linear Optimization, §7.5

Anany Levitin, Introduction to the Design and Analysis of Algorithms, §10.2

Cryonics as Memoir

I wonder if cryonics would have a better reputation if it were sold as being more like leaving a memoir, than a bid for personal immortality. Historians are glad to have Samuel Pepys's diary for all that it tells us about life in 1660s London; would they not be more overjoyed to have Samuel Pepys's brain, if only we knew how to read brains as easily as we can read books?

Talking Too Much

"Sorry, have I been dominating our conversations too much?"

"Not at all; why do you ask?"

"I feel like my model of you is less detailed than my model of your model of me."

Lyrics to the Song About Truthseekers

Yesterday my sister won the Nobel prize
Her work will be a benefit to all of humankind
She went and proved some things which no one had surmised
Yesterday my sister won the Nobel physics prize

Yesterday I dreamed I won the Pulitzer prize
I uncovered the scandal, was a President's demise
I found the truth and brought it out to people's eyes
Yesterday I dreamed I won the Pulitzer journalism prize

And then I woke up
And rubbed my eyes

Yesterday my sister won the Nobel prize
Her work will be a benefit to all of humankind
She went and proved some things which no one had surmised
Yesterday my sister won the Nobel physics prize

The History of the Universe

"So, the universe starts out being made out of physics, then turns into game theory as life, then civilization, then artificial intelligence do increasingly a priori improbable things, then turns back into physics again as everyone runs out of negentropy. Poetically speaking."

"Right. Literally, it would just be physics throughout."

"But the poetry pays rent: the more agent-like a process is, the more predictively useful it is to take the intentional stance of talking about what it wants, rather than computing out the physics."

Personhood

"Did you hear that India has recognized dolphins as nonhuman persons with rights to life and liberty?"

"Hm, yes."

"I thought you'd approve."

"Oh, I do; any measure that reduces the suffering of sentient creatures is a very noble thing. I'm just uncertain about the correct way to generalize the concept of personhood in the wake of the knowledge that humanity isn't ontologically privileged. Some people seem to favor a broad interpretation, such that chimps and dolphins are persons. But sometimes I wonder if a narrow construal would be better, where not all humans are persons."

"You mean, excluding infants and brain-damage cases?"

"No, I mean excluding us. After you filter through all the memetic noise and tribal posturing, real-life humans don't exhibit nearly as much moral agency as we like to think. We have not yet dreamed of what our highest ideals of personhood would look like when implemented consistently."