Tuesday, 9 December 2014

walking our grid

Last time we defined a grid structure. This time we are going to walk it (perhaps emulating what happens if we have a single ant leaving a scent trail as it walks).
Let's jump right into the code:

First, we add this line in to the learn a grid for loop from yesterday:
c.learn("cell-value",elt,"0")
ie, define the value 0 for every location in our grid.

Then some code to output our grid:
def string_grid(c,grid):
  I = int(grid.apply_op(c,"dim-1").label)
  J = int(grid.apply_op(c,"dim-2").label)

  s = ""
  s += "I: " + str(I) + "\n"
  s += "J: " + str(J) + "\n"

  for j in range(1,J+1):
    s += str(j).ljust(4)
    for i in range(1,I+1):
      x = ket_elt(j,i)
      value = x.apply_op(c,"cell-value").the_label()
      if value == "0":
        value = "."
      s += value.rjust(3)
    s += "\n"
  return s
Then we want to implement something like this to walk the grid, though that would require the non-existent swc language (because of the repeat n: loop) that we intend as a wrapper around the current BKO:
next |*> #=> pick-elt (SW |_self> + S |_self> + SE |_self>)
|cell> => |grid: 1 22>
repeat n:
  cell-value "" |cell> => arithmetic(cell-value |_self>,|+>,|1>)
  |cell> => next "" |cell> 
Instead we have this python:
# set context: 
C = context_list("walking a grid")

# create a 20*20 grid:
create_grid(C,20,20)

# define the grid ket:
grid = ket("grid")

# number of steps:
n = 20

# the seed/starting cell:
seed_cell = ket("grid: 10 10")

# define some cell propagation rules:
C.learn("SW-S-SE","*",stored_rule("pick-elt (SW |_self> + S |_self> + SE |_self>)"))
C.learn("W-SW-S-SE-E","*",stored_rule("pick-elt (W |_self> + SW |_self> + S |_self> + SE |_self> + E |_self>)"))
C.learn("W-SW-S-SE-E-NE","*",stored_rule("pick-elt (W |_self> + SW |_self> + S |_self> + SE |_self> + E |_self> + NE |_self>)"))
C.learn("NW-W-SW-S-SE-E","*",stored_rule("pick-elt (NW |_self> + W |_self> + SW |_self> + S |_self> + SE |_self> + E |_self>)"))
C.learn("NW-W-SW-S-SE-E-NE","*",stored_rule("pick-elt (NW |_self> + W |_self> + SW |_self> + S |_self> + SE |_self> + E |_self> + NE |_self>)"))

# define our walk_grid() function:
def walk_grid(C,seed_cell,next,steps):
  C.learn("next-value","*",stored_rule("arithmetic(cell-value |_self>,|+>,|1>)"))

  cell = seed_cell
  for k in range(steps):
    next_value = cell.apply_op(C,"next-value")
    C.learn("cell-value",cell,next_value)
    cell = cell.apply_op(C,next).ket() 


# walk the grid, noting the data is all stored in our context variable C:
# and note in this case we use the "SW-S-SE" propagation rule.
# ie, randomly pick from SW, S and SE.
walk_grid(C,seed_cell,"SW-S-SE",30)

# print it out:
print(string_grid(C,grid))
And we get something like this:
1     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
2     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
3     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
4     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
5     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
6     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
7     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
8     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
9     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
10    .  .  .  .  .  .  .  .  .  1  .  .  .  .  .  .  .  .  .  .
11    .  .  .  .  .  .  .  .  .  1  .  .  .  .  .  .  .  .  .  .
12    .  .  .  .  .  .  .  .  .  1  .  .  .  .  .  .  .  .  .  .
13    .  .  .  .  .  .  .  .  .  1  .  .  .  .  .  .  .  .  .  .
14    .  .  .  .  .  .  .  .  .  .  1  .  .  .  .  .  .  .  .  .
15    .  .  .  .  .  .  .  .  .  .  1  .  .  .  .  .  .  .  .  .
16    .  .  .  .  .  .  .  .  .  1  .  .  .  .  .  .  .  .  .  .
17    .  .  .  .  .  .  .  .  1  .  .  .  .  .  .  .  .  .  .  .
18    .  .  .  .  .  .  .  .  1  .  .  .  .  .  .  .  .  .  .  .
19    .  .  .  .  .  .  .  .  .  1  .  .  .  .  .  .  .  .  .  .
20    .  .  .  .  .  .  .  .  . 20  .  .  .  .  .  .  .  .  .  .
Note we ran into the wall because we are using a finite universe model. With torus model it would wrap around to the top of the grid. To see how these alternatives are implemented see the ket_elt_bd(j,i,I,J) code from yesterday.

Next, try another example:
-- a new seed_cell:
seed_cell = ket("grid: 2 10")
-- the "W-SW-S-SE-E" propagation rule.
-- and 200 steps:
walk_grid(C,seed_cell,"W-SW-S-SE-E",200)
print(string_grid(C,grid))
1     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
2     .  .  .  .  .  .  .  .  .  1  .  .  .  .  .  .  .  .  .  .
3     .  .  .  .  .  .  .  .  1  .  .  .  .  .  .  .  .  .  .  .
4     .  .  .  .  .  .  .  .  .  1  1  .  .  .  .  .  .  .  .  .
5     .  .  .  .  .  .  .  .  .  1  1  1  1  .  .  .  .  .  .  .
6     .  .  .  .  .  .  .  .  .  .  .  .  1  1  .  .  .  .  .  .
7     .  .  .  .  .  .  .  .  .  .  .  .  1  .  .  .  .  .  .  .
8     .  .  .  .  .  .  .  .  .  .  .  1  1  2  2  1  .  .  .  .
9     .  .  .  .  .  .  .  .  .  .  .  .  1  1  .  .  .  .  .  .
10    .  .  .  .  .  .  .  .  .  .  .  .  2  1  .  .  .  .  .  .
11    .  .  .  .  .  .  .  .  .  .  .  .  .  1  .  .  .  .  .  .
12    .  .  .  .  .  .  .  .  .  .  .  .  .  .  1  1  .  .  .  .
13    .  .  .  .  .  .  .  .  .  .  .  .  1  2  1  .  .  .  .  .
14    .  .  .  .  .  .  .  .  .  .  .  .  1  1  .  .  .  .  .  .
15    .  .  .  .  .  .  .  .  .  .  .  .  1  1  .  .  .  .  .  .
16    .  .  .  .  .  .  .  .  .  .  .  .  1  1  .  .  .  .  .  .
17    .  .  .  .  .  .  .  .  .  .  .  .  .  1  .  .  .  .  .  .
18    .  .  .  .  .  .  .  .  .  .  .  1  1  .  .  .  .  .  .  .
19    .  .  .  .  .  .  .  .  .  .  .  1  1  .  .  .  .  .  .  .
20    .  .  .  .  2  8 11  8  7 14 15 12 14 14 11  6  6 12 13  6
I guess that is enough of an example. A couple of notes:
1) the above propagation rules are quite stupid. They just pick randomly from available directions for a given grid cell. In practice you would have rules with more intelligence. Exactly how you would implement these more intelligent propagation rules I'm not 100% sure yet.
2) It is easy enough to associate values with grid locations.
eg, something like this:
C.learn("cell-value","grid: 3 5","13")
C.learn("cell-value","grid: 7 2","8")
C.learn("cell-value","grid: 1 5","H")
C.learn("cell-value","grid: 10 8","G")
C.learn("cell-value","grid: 10 9","R")
C.learn("cell-value","grid: 10 10","I")
C.learn("cell-value","grid: 10 11","D")
print(string_grid(C,grid))

-- giving this output:
I: 20
J: 20
1     .  .  .  .  H  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
2     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
3     .  .  .  . 13  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
4     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
5     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
6     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
7     .  8  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
8     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
9     .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
10    .  .  .  .  .  .  .  G  R  I  D  .  .  .  .  .  .  .  .  .
11    .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
12    .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
13    .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
14    .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
15    .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
16    .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
17    .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
18    .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
19    .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
20    .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
And of course, you can associate arbitrary superpositions with grid locations (something we will do later!), they are just harder to display. Indeed, the above cell values are actually kets, since context.learn(a,b,c) auto-casts c to a ket if it is a string.

OK. I dug up an example of mapping locations to superpositions. Here we have pixel locations mapped to rgb values, using the Lenna image.
eg a few sample pixels to superpositions:
pixel-value |pixel: 0: 29> => 236.000|r> + 147.000|g> + 116.000|b> + 255.000|a>
pixel-value |pixel: 0: 30> => 238.000|r> + 149.000|g> + 122.000|b> + 255.000|a>
pixel-value |pixel: 0: 31> => 238.000|r> + 150.000|g> + 123.000|b> + 255.000|a>
pixel-value |pixel: 0: 32> => 239.000|r> + 147.000|g> + 119.000|b> + 255.000|a>
pixel-value |pixel: 0: 33> => 236.000|r> + 141.000|g> + 116.000|b> + 255.000|a>
And that should be it for now. More in my next post!

Update: uploaded the make grid code to here.

Monday, 8 December 2014

learning a grid

Now, an idea motivated by the concept of grid cells in rat brains.
So, we define a big grid using this python:
c = new_context("grid play")

def ket_elt(j,i):
  return ket("grid: " + str(j) + " " + str(i))

# Makes use of the fact that context.learn() ignores rules that are the empty ket |>.
def ket_elt_bd(j,i,I,J):
# finite universe model:
  if i <= 0 or j <= 0 or i > I or j > J:
    return ket("",0)
# torus model:
#  i = (i - 1)%I + 1
#  j = (j - 1)%J + 1
  return ket("grid: " + str(j) + " " + str(i))


def create_grid(c,I,J):
  c.learn("dim-1","grid",str(I))
  c.learn("dim-2","grid",str(J))

  for j in range(1,J+1):
    for i in range(1,I+1):
      elt = ket_elt(j,i)
      c.add_learn("elements","grid",elt)
      c.learn("N",elt,ket_elt_bd(j-1,i,I,J))
      c.learn("NE",elt,ket_elt_bd(j-1,i+1,I,J))
      c.learn("E",elt,ket_elt_bd(j,i+1,I,J))
      c.learn("SE",elt,ket_elt_bd(j+1,i+1,I,J))
      c.learn("S",elt,ket_elt_bd(j+1,i,I,J))
      c.learn("SW",elt,ket_elt_bd(j+1,i-1,I,J))
      c.learn("W",elt,ket_elt_bd(j,i-1,I,J))
      c.learn("NW",elt,ket_elt_bd(j-1,i-1,I,J))
And once we run this, we have example grid locations such as:
supported-ops |grid: 4 39> => |op: N> + |op: NE> + |op: E> + |op: SE> + |op: S> + |op: SW> + |op: W> + |op: NW>
N |grid: 4 39> => |grid: 3 39>
NE |grid: 4 39> => |grid: 3 40>
E |grid: 4 39> => |grid: 4 40>
SE |grid: 4 39> => |grid: 5 40>
S |grid: 4 39> => |grid: 5 39>
SW |grid: 4 39> => |grid: 5 38>
W |grid: 4 39> => |grid: 4 38>
NW |grid: 4 39> => |grid: 3 38>

supported-ops |grid: 4 40> => |op: N> + |op: NE> + |op: E> + |op: SE> + |op: S> + |op: SW> + |op: W> + |op: NW>
N |grid: 4 40> => |grid: 3 40>
NE |grid: 4 40> => |grid: 3 41>
E |grid: 4 40> => |grid: 4 41>
SE |grid: 4 40> => |grid: 5 41>
S |grid: 4 40> => |grid: 5 40>
SW |grid: 4 40> => |grid: 5 39>
W |grid: 4 40> => |grid: 4 39>
NW |grid: 4 40> => |grid: 3 39>
So, what is the point of this? Well, once you have a grid defined (or more generally, any topology of interest. eg for a mobile robot in a hospital you can map out the locations of corridors and walls), you can then associate objects with locations.
eg:
value |grid: 4 25> => |39>
building |grid: 30 22> => |building: hotel>
smell |grid: 5 7> => |smell: dead fish>
place |grid: 13 17> => |my place of work>
place |grid: 57 97> => |my home>

eg, "what is two steps north of the hotel?"
N N inverse-building |building: hotel>

eg, "what is seven steps SE of the dead fish smell?"
SE^7 inverse-smell |smell: dead fish>

And you can use it to update your knowledge of position (say when dead-reckoning):
|current location> => inverse-building |building: hotel>

I guess that is about it. This is a very powerful construct, and I don't mean just a grid topology, I mean for the case of more general topologies (eg a calendar!).

Update: Indeed, we can define a near current location too.
Something like:
-- and for other topologies, we define different near operators.
sa: near |grid: *> #=> |_self> + N|_self> + NE|_self> + E|_self> + SE|_self> + S|_self> + SW|_self> + W|_self> + NW|_self>
sa: building |grid: 4 40> => |building: cafe>
sa: create inverse
sa: current |location> => inverse-building |building: cafe>
sa: near-current |location> => near inverse-building |building: cafe>

-- now ask:
sa: current |location>
|grid: 4 40>

sa: near-current |location>
|grid: 4 40> + |grid: 3 40> + |grid: 3 41> + |grid: 4 41> + |grid: 5 41> + |grid: 5 40> + |grid: 5 39> + |grid: 4 39> + |grid: 3 39>

-- alternatively, we can save a step:
sa: near current |location>
|grid: 4 40> + |grid: 3 40> + |grid: 3 41> + |grid: 4 41> + |grid: 5 41> + |grid: 5 40> + |grid: 5 39> + |grid: 4 39> + |grid: 3 39>

-- "what is 13 steps NW of your current location?"
NW^13 current |location>
Neat!

Update: And we can easily enough define alias's for north and so on:
north |*> #=> N |_self>
north-east |*> #=> NE |_self>
east |*> #=> E |_self>
south-east |*> #=> SE |_self>
south |*> #=> S |_self>
south-west |*> #=> SW|_self>
west |*> #=> W |_self>
north-west |*> #=> NW|_self>
Update: an explanation about what I mean by a calendar topology. So, just like grid elements can be linked by north, south, east, west, and so on. A calendar has next-hour, previous-hour, tomorrow, yesterday, next-week, next-thursday, last-friday, etc. Then we build up a generic calendar, with time/date slots all linked by those operators, and then later you associate appointments, and so on with each calendar slot.

to-base()

This is a quick one. Just some code to convert from base 10 to a base of your choice.
First the python:
# convert decimal number to base b.
# number, base need to be kets. category_number_to_number() should take care of that.
def decimal_to_base(number,base):
  r = int(category_number_to_number(number).value)
  b = int(category_number_to_number(base).value)
#  print("r:",r)
#  print("b:",b)
  current_base = 1
  result = superposition()
  while r > 0:
    rem = r%b
    r //= b
    result += ket(str(current_base),rem)
    current_base *= b
  return result  
Now a couple of examples:
sa: to-base(|255>,|2>)
|1> + |2> + |4> + |8> + |16> + |32> + |64> + |128> 

sa: to-base(|213>,|2>)
|1> + 0.000|2> + |4> + 0.000|8> + |16> + 0.000|32> + |64> + |128>

sa: to-base(|359822>,|10>)
2.000|1> + 2.000|10> + 8.000|100> + 9.000|1000> + 5.000|10000> + 3.000|100000>
Probably don't need any more examples than that, the idea is simple enough!

Update: now with the magic of tables:
sa: table[base,coeff] to-base(|255>,|2>)
+------+-------+
| base | coeff |
+------+-------+
| 1    | 1     |
| 2    | 1     |
| 4    | 1     |
| 8    | 1     |
| 16   | 1     |
| 32   | 1     |
| 64   | 1     |
| 128  | 1     |
+------+-------+

sa: table[base,coeff] to-base(|213>,|2>)
+------+-------+
| base | coeff |
+------+-------+
| 1    | 1     |
| 2    | 0     |
| 4    | 1     |
| 8    | 0     |
| 16   | 1     |
| 32   | 0     |
| 64   | 1     |
| 128  | 1     |
+------+-------+

sa: table[base,coeff] to-base(|359822007>,|10>)
+-----------+-------+
| base      | coeff |
+-----------+-------+
| 1         | 7     |
| 10        | 0     |
| 100       | 0     |
| 1000      | 2     |
| 10000     | 2     |
| 100000    | 8     |
| 1000000   | 9     |
| 10000000  | 5     |
| 100000000 | 3     |
+-----------+-------+
Nice.

Update: now with the magic of "to-comma-number":
sa: table[base,coeff] to-comma-number to-base(|10973498714>,|2>)
+---------------+-------+
| base          | coeff |
+---------------+-------+
| 1             | 0     |
| 2             | 1     |
| 4             | 0     |
| 8             | 1     |
| 16            | 1     |
| 32            | 0     |
| 64            | 1     |
| 128           | 0     |
| 256           | 1     |
| 512           | 0     |
| 1,024         | 1     |
| 2,048         | 1     |
| 4,096         | 0     |
| 8,192         | 0     |
| 16,384        | 1     |
| 32,768        | 0     |
| 65,536        | 0     |
| 131,072       | 1     |
| 262,144       | 0     |
| 524,288       | 0     |
| 1,048,576     | 1     |
| 2,097,152     | 0     |
| 4,194,304     | 0     |
| 8,388,608     | 0     |
| 16,777,216    | 0     |
| 33,554,432    | 1     |
| 67,108,864    | 1     |
| 134,217,728   | 1     |
| 268,435,456   | 0     |
| 536,870,912   | 0     |
| 1,073,741,824 | 0     |
| 2,147,483,648 | 1     |
| 4,294,967,296 | 0     |
| 8,589,934,592 | 1     |
+---------------+-------+

algebra

We have arithmetic, so how about some simple algebra? The code is kind of ugly, but it works well enough.
Let's jump in, here is the python:
# maps ket -> ket
# 3|x> => 3|x>
# |number: 7.2> => 7.2| >  # NB: the space in the ket label.
# 2|number: 3> => 6| >     # We can't use just |> because it is dropped all over the place!
# 8|number: text> => 0| >  # so the maths eqn: 3a + 7
# |3.7> => 3.7| >          # in my notation is 3|a> + 7| >
# 3|5> => 15| >
def category_number_to_number(one):         # find better name!
  one = one.ket()
  cat, value = extract_category_value(one.label)
  try:
    n = float(value)
  except:
    if cat == 'number':                     # not 100% want to keep these two lines
      return ket(" ",0)
    return one
  return ket(" ",one.value * n) 

def algebra_add(one,two):
  return one + two

def algebra_subtract(one,two):
  return delete3(one,two)

def algebra_mult(one,two,Abelian=True):
  one = superposition() + one  # hack so one and two are definitely sp, not ket
  two = superposition() + two

  result = superposition()
  for x in one.data:           # another hack! We really need to define an iterator for superpositions.
    x = category_number_to_number(x)  
    for y in two.data:
      y = category_number_to_number(y)
      print("x*y",x,"*",y)
      labels = [ L for L in x.label.split('*') + y.label.split('*') if L.strip() != '' ]
      if Abelian:  
        labels.sort()
      label = "*".join(labels)
      if label == '':         # we can't have ket("",value), since it will be dropped.
        label = " "
      result += ket(label,x.value * y.value)
  return result

def algebra_power(one,two,Abelian=True):
  one = superposition() + one
  two = category_number_to_number(two)
  try:
    n = int(two.value)
  except:
    return ket(" ",0)

  if n <= 0:
    return ket(" ",1)

  result = one
  for k in range(n - 1):
    result = algebra_mult(result,one,Abelian)
  return result

# implement basic algebra:
def algebra(one,operator,two,Abelian=True):
  op_label = operator if type(operator) == str else operator.the_label()
  null, op = extract_category_value(op_label)

  if op not in ['+','-','*','^']:
    return ket(" ",0)

  if op == '+':
    return algebra_add(one,two)            # Abelian option here too?
  elif op == '-':
    return algebra_subtract(one,two)       # ditto.
  elif op == '*':
    return algebra_mult(one,two,Abelian)
  elif op == '^':
    return algebra_power(one,two,Abelian)
  else:
    return ket(" ",0)
OK. So with that mess out of the way, let's give some examples:
-- 3x * 5y
sa: algebra(3|x>,|*>,5|y>)
15.000|x*y>

-- (3x^2 + 7x + 13) * 5y
sa: algebra(3|x*x> + 7|x> + 13| >,|*>,5|y>)
15.000|x*x*y> + 35.000|x*y> + 65.000|y> 

-- (2x^2 + 11x + 19) * (11y + 13)
sa: algebra(2|x*x> + 11|x> + 19| >,|*>,11|y> + 13| >)
22.000|x*x*y> + 26.000|x*x> + 121.000|x*y> + 143.000|x> + 209.000|y> + 247.000| >

-- (a + b)^2
sa: algebra(|a> + |b>,|^>,|2>)
|a*a> + 2.000|a*b> + |b*b>

-- (a + b)^5
sa: algebra(|a> + |b>,|^>,|5>)
|a*a*a*a*a> + 5.000|a*a*a*a*b> + 10.000|a*a*a*b*b> + 10.000|a*a*b*b*b> + 5.000|a*b*b*b*b> + |b*b*b*b*b>

-- (a + b + c + d)^3
sa: algebra(|a> + |b> + |c> + |d>,|^>,|3>)
|a*a*a> + 3.000|a*a*b> + 3.000|a*a*c> + 3.000|a*a*d> + 3.000|a*b*b> + 6.000|a*b*c> + 6.000|a*b*d> + 3.000|a*c*c> + 6.000|a*c*d> + 3.000|a*d*d> + |b*b*b> + 3.000|b*b*c> + 3.000|b*b*d> + 3.000|b*c*c> + 6.000|b*c*d> + 3.000|b*d*d> + |c*c*c> + 3.000|c*c*d> + 3.000|c*d*d> + |d*d*d>
I guess that is about it. Perhaps I should mention I also have complex number multiplication, but it is not wired into the processor, so we can't use it in the console. Anyway, here is that code:
# simple complex number mult:
def complex_algebra_mult(one,two):
  one = superposition() + one  # hack so one and two are definitely sp, not ket
  two = superposition() + two

  result = superposition()
  for x in one.data:
    for y in two.data:
      if x.label == 'real' and y.label == 'real':
        result += ket("real",x.value * y.value)

      if x.label == 'real' and y.label == 'imag':
        result += ket("imag",x.value * y.value)

      if x.label == 'imag' and y.label == 'real':
        result += ket("imag",x.value * y.value)

      if x.label == 'imag' and y.label == 'imag':
        result += ket("real",-1 * x.value * y.value)
  return result
And that is it for now! More maths in the next post I think.

Update: We can even implement say f(x) = 3x^2 + 5x + 19.
f |*> #=> 3 algebra(""|_self>,|^>,|2>) + 5 ""|_self> + 19| >

-- now, let's test it:
-- first a simple one:
sa: |x> => |a>
sa: f |x>
3.000|a*a> + 5.000|a> + 19.000| >

-- now a slightly more interesting one:
sa: |x> => |a> + |b>
sa: f |x>
3.000|a*a> + 6.000|a*b> + 3.000|b*b> + 5.000|a> + 5.000|b> + 19.000| >
Neat!

Update: OK. What about function composition? Well we have to do that a little indirectly to side-step the linearity of operators.
Say:
g(x) = 7 x^2
-- define our functions:
sa: f |*> #=> 3 algebra(""|_self>,|^>,|2>) + 5 ""|_self> + 19| >
sa: g |*> #=> 7 algebra(""|_self>,|^>,|2>)

-- define x:
sa: |x> => |a>

-- f(x):
sa: f |x>
3.000|a*a> + 5.000|a> + 19.000| >

-- g(x):
sa: g |x>
7.000|a*a>

-- now a first try at g(f(x)):
sa: g f |x>
|>
-- Doh! I didn't expect that.
-- The explanation is the "" |_self> applied to |a*a>, |a> and then | >.

-- now, try again, this time indirectly:
sa: |y> => f |x>
sa: g |y>
63.000|a*a*a*a> + 210.000|a*a*a> + 973.000|a*a> + 1330.000|a> + 2527.000| >
Another update: OK. Here is one way to show the linearity I expected last time:
-- NB: |_self> and not "" |_self>
sa: h |*> #=> 2 algebra(|_self>,|^>,|3>)
sa: h |x>
2.000|x*x*x>
sa: h (|a> + |b>)
2.000|a*a*a> + 2.000|b*b*b>
Update: now with the magic of tables, and the new display-algebra operator:
-- f(x) = 3x^2 + 5x +  19
sa: f |*> #=> 3 algebra(""|_self>,|^>,|2>) + 5 ""|_self> + 19| >
sa: |x> => |a>
sa: |y> => |a> + |b>
sa: |z> => |a> + |b> + |c>
sa: display-f |*> #=> display-algebra f |_self>
sa: table[ket,display-f] split |x y z>
+-----+----------------------------------------------------------------------+
| ket | display-f                                                            |
+-----+----------------------------------------------------------------------+
| x   | 3*a*a + 5*a + 19                                                     |
| y   | 3*a*a + 6*a*b + 3*b*b + 5*a + 5*b + 19                               |
| z   | 3*a*a + 6*a*b + 6*a*c + 3*b*b + 6*b*c + 3*c*c + 5*a + 5*b + 5*c + 19 |
+-----+----------------------------------------------------------------------+
Cool!

set builder in BKO

OK. It seems to me that it would be useful to have the equivalent of set-builder/list comprehensions in BKO. Currently not even close to implemented in the parser, but never mind.

The thing is, a lot of things you think you need a set-builder for, you can actually do directly.

Let's consider some data:
$ grep "^president-era" early-us-presidents.sw
president-era |Washington> => |year: 1789> + |year: 1790> + |year: 1791> + |year: 1792> + |year: 1793> + |year: 1794> + |year: 1795> + |year: 1796> + |year: 1797>
president-era |Adams> => |year: 1797> + |year: 1798> + |year: 1799> + |year: 1800> + |year: 1801>
president-era |Jefferson> => |year: 1801> + |year: 1802> + |year: 1803> + |year: 1804> + |year: 1805> + |year: 1806> + |year: 1807> + |year: 1808> + |year: 1809>
president-era |Madison> => |year: 1809> + |year: 1810> + |year: 1811> + |year: 1812> + |year: 1813> + |year: 1814> + |year: 1815> + |year: 1816> + |year: 1817>
president-era |Monroe> => |year: 1817> + |year: 1818> + |year: 1819> + |year: 1820> + |year: 1821> + |year: 1822> + |year: 1823> + |year: 1824> + |year: 1825>
president-era |Q Adams> => |year: 1825> + |year: 1826> + |year: 1827> + |year: 1828> + |year: 1829>
Now we can ask: "Who was the president in 1825?"
-- initially you might try something like this:
|x> in "" |early US Presidents: _list> such that <year: 1825|president-era|x> > 0.5

-- but using inverse we can do it directly:
inverse-president-era |year: 1825>
Indeed, let's do it:
sa: load early-us-presidents.sw
sa: create inverse
sa: inverse-president-era |year: 1825>
|Monroe> + |Q Adams> 
Now, let's consider some more data:
$ grep "^president-number" early-us-presidents.sw
president-number |Washington> => |number: 1>
president-number |Adams> => |number: 2>
president-number |Jefferson> => |number: 3>
president-number |Madison> => |number: 4>
president-number |Monroe> => |number: 5>
president-number |Q Adams> => |number: 6>
And now we might ask: "What was the party of the third president?"
-- again, you might ask it in set-builder form:
party |x> for |x> in "" |early US Presidents: _list> such that president-number|x> == |number: 3>

-- but once again, it is cleaner and easier to do it directly:
sa: inverse-president-number |number: 3>
|Jefferson>

sa: party inverse-president-number |number: 3>
|party: Democratic-Republican> 
Anyway, the general proposed form for set-builder in BKO is something like this:
KET in SUPERPOSITION such that TRUTH-STATEMENT
and
OP-SEQUENCE KET for KET in SUPERPOSITION such that TRUTH-STATEMENT
And I'm not 100% sure on using "such that". Also considering "st", "such-that", and "where".

And I guess that is about it.
OK. How about some notes first:
1) notice how easy it is to grep sw files to extract what you are interested in. This is the big win from having no multi-line constructs in BKO. Heh, and also the reason why \r and \n are not allowed inside bras and kets.
2) Note how the direct version always mentions "inverse-OP" (here "inverse-president-era" and "inverse-president-number"). This is probably to be expected because it is replacing a set builder, and set builders are in some sense an inverse. I need to explain this clearer sometime.
3) the BKO set-builder notation is much closer to standard SQL and SPARQL than the direct method.

Update: Let's try and expand note (2).
Consider:
{x in R | f(x) = y }
this is the same as the inverse function:
x = f^-1(y)

Likewise, in BKO:
|x> in "" |number: _list> such that foo |x> == |y>
this is the same as the inverse direct version:
inverse-foo |y>

Here, let me show a quick example in the console:
sa: foo |a1> => |y>
sa: foo |a2> => |y>
sa: foo |a3> => |y>
sa: foo |a4> => |y>
sa: foo |a5> => |fish>
sa: foo |a6> => |soup>
sa: foo |a7> => |fish>
sa: create inverse

sa: inverse-foo |y>
|a1> + |a2> + |a3> + |a4>

sa: inverse-foo |fish>
|a5> + |a7>

sa: inverse-foo |soup>
|a6>
Update: in note (1) above I noted how easy it is to grep sw files. Well, it just occurred to me that it is easy to find supported operators in a sw file.
eg:
$ sed 's/|.*$//g' early-us-presidents.sw | sort | uniq

----------------------------------------

dissolved
founded
full-name
party
president-era
president-number
supported-ops

$ sed 's/|.*$//g' improved-imdb.sw | sort | uniq

----------------------------------------
actors
movies
supported-ops

$ sed 's/|.*$//g' imdb-ratings.sw | sort | uniq
imdb-rating
imdb-rating-self
imdb-votes
imdb-votes-self
Cool and useful!

Sunday, 7 December 2014

simple finite sets and soft intersection

OK. We have defined union and intersection, so it is obvious we can represent simple finite sets in BKO. I'm not sure of the case for more complex sets.
Anyway, something like:
In standard set notation:
A = {a1,a2,a3,ab,ac,abc}
B = {b1,b2,b3,ab,bc,abc}
C = {c1,c2,c3,ac,bc,abc}
And in BKO:
-- NB: coeffs for set elements are almost always in {0,1}
|A> => |a1> + |a2> + |a3> + |ab> + |ac> + |abc>     
|B> => |b1> + |b2> + |b3> + |ab> + |bc> + |abc>
|C> => |c1> + |c2> + |c3> + |ac> + |bc> + |abc>
Now some simple examples of intersection (union is trivial/obvious/boring):
sa: intersection(""|A>, ""|B>)
|ab> + |abc>

sa: intersection(""|A>, ""|C>)
|ac> + |abc>

sa: intersection(""|B>, ""|C>)
|bc> + |abc>

sa: intersection(""|A>,""|B>,""|C>)
|abc>
Now, let's observe what happens when we add our sets:
a: "" |A> + "" |B> + "" |C>
|a1> + |a2> + |a3> + 2.000|ab> + 2.000|ac> + 3.000|abc> + |b1> + |b2> + |b3> + 2.000|bc> + |c1> + |c2> + |c3>
Since our sets only have coeffs of 0 of 1, then when we add sets, the coeff of each ket corresponds to how many sets that object is in (cf. the Venn diagram above). This is kind of useful. Indeed, even if we add "non-trivial" superpositions (ie, those with coeffs outside of {0,1}), the resulting coeffs are also interesting. Anyway, an example of that later!

Next thing I want to add is the idea of a "soft" intersection. The motivation is that intersection is close, though maybe not exactly, what happens when a child learns. Say each time a parent sees a dog, they point and say to their child "dog". Perhaps mentally what is going on is that each instance of this, the child does a set intersection of everything in mind at the time the child hears "dog". OK. Works kind of OK. But what happens if the parent says dog and there is no dog around? Standard intersection is brutal, one wrong example in the training set and suddenly the learnt pattern becomes the empty set! And hence the motivation for the idea of a "soft intersection".
-- this is similar to standard intersection:
sa: drop-below[3] (""|A> + ""|B> + ""|C>)
3.000|abc>

-- clean is a sigmoid that maps all non-zero coeffs to 1.
-- this is identical to standard intersection (at least for the case all coeffs are 0 or 1):
sa: clean drop-below[3] (""|A> + ""|B> + ""|C>)
|abc>

-- now consider this object:
sa: drop-below[2] (""|A> + ""|B> + ""|C>)
2.000|ab> + 2.000|ac> + 3.000|abc> + 2.000|bc>

-- and finally, here is our soft intersection:
sa: clean drop-below[2] (""|A> + ""|B> + ""|C>)
|ab> + |ac> + |abc> + |bc>
So the assumed general pattern for learning is:
-- strict intersection version:
pattern |object: X> => common[pattern] (|object: X: example: 1> + |object: X: example: 2> + |object: X: example: 3> + |object: X: example: 4> + |object: X: example: 5> + ... )

-- softer intersection version:
pattern |object: X> => drop-below[t0] (drop-below[t1] pattern |object: X: example: 1> + drop-below[t2] pattern |object: X: example: 2> + drop-below[t3] pattern |object: X: example: 3> + drop-below[t4] pattern |object: X: example: 4> + drop-below[t5] pattern |object: X: example: 5> + ... )
For some set of parameters {t0, t1, t2, ...}
And I guess that is it for now.

set union and intersection in BKO

So, for kets with coeffs in {0,1} then union and intersection work in the standard way.
eg:
sa: union(|a> + |c> + |d> + |f>, |a> + |b> + |d> + |e>)
|a> + |c> + |d> + |f> + |b> + |e>

sa: intersection (|a> + |c> + |d> + |f>, |a> + |b> + |d> + |e>)
|a> + |d>
Simple enough, but what happens when coeffs take values other than 0 or 1?
Well, we need to generalize union and intersection.
One possible generalization is intersection maps to min(a,b) and union to max(a,b), while noting that min(0,1) = 0, and max(0,1) = 1, and hence reproduces the standard definition of intersection and union.
Let's give a couple of very simple examples:
sa: union(3|a>, 10|a>)
10.000|a>

sa: intersection(3|a>,10|a>)
3.000|a>
Now some (slightly) more interesting examples:
sa: union(3|a> + 2|c> + 0.7|d> + 0|x>,0.9|a> + 7.2|c> + 33.3|e> + |x>)
3.000|a> + 7.200|c> + 0.700|d> + |x> + 33.300|e>

sa: intersection(3|a> + 2|c> + 0.7|d> + 0|x>,0.9|a> + 7.2|c> + 33.3|e> + |x>)
0.900|a> + 2.000|c>
So, what is the use of intersection and union?
A couple of common examples are:
"what movies do actor x and actor y have in common?"
intersection(movies |actor: x>, movies |actor: y>)

"what friends do Fred and Sam have in common?"
intersection(friends |Fred>, friends |Sam>)

Indeed, common enough that I added in some short-cut notation:
common[movies] (|actor: x> + |actor: y>)
common[friends] (|Fred> + |Sam>)

Here, let me give a quick example:
-- define some knowledge about friends:
sa: friends |Fred> => |Alice> + |Barry> + |Bill> + |George> + |Rob>
sa: friends |Sam> => |Alice> + |Rob> + |Emma> + |Steve>
sa: common[friends] (|Fred> + |Sam>)
|Alice> + |Rob> 
Now a quick observation:
There seems to a recurring pattern between lists in English and superpositions (as we see above with "actor x and actor y" and "Fred and Sam"):
eg:
"x and y" <=> |x> + |y>
"x, y and z" <=> |x> + |y> + |z>
"a, b, c, d and e" <=> |a> + |b> + |c> + |d> + |e>

Indeed, I already have code to map superpositions back to lists:
sa: list-to-words (|apples> + |oranges> + |coffee> + |milk>)
|text: apples, oranges, coffee and milk>
Though I do not yet have code for English to superpositions (yet), nor the code to handle the coeffs in the superposition. eg, would be nice to have: 3|apples> + 2|oranges> to map to "3 apples and 2 oranges".

Anyway, final comment is to note how close the English is to the BKO:

"what friends do Fred and Sam have in common?"
common[friends] (|Fred> + |Sam>)

Or, more generally:
"what OP do X, Y and Z have in common?"
common[OP] (|X> + |Y> + |Z>)

The plan is to eventually write a parser to map English to BKO, but that is a long way off yet!

different objects, identical network structure

In this post a quick example showing that completely different objects can have identical network/matrix structure.

So, let's look at some BKO:
friends |Alex> => |Jason> + |Ed> + |Mary> + |Liz> + |Beth> + |James> + |nathan>
friends |Bill> => |Jason> + |Beth> + |lena> + |John> + |nathan>
friends |Harry> => |charlie> + |bella> + |sam> + |smithie> + |david> + |nathan>

links-to |url 1> => |url k> + |url g> + |url b> + |url f> + |url l> + |url e> + |url j>
links-to |url 2> => |url h> + |url l> + |url b> + |url g> + |url i>
links-to |url 3> => |url m> + |url a> + |url d> + |url c> + |url n> + |url l>
So, they look a little alike, but not identical. But check out their matrix representations:
sa: matrix[friends]
[ bella   ] = [  0     0     1.00  ] [ Alex  ]
[ Beth    ]   [  1.00  1.00  0     ] [ Bill  ]
[ charlie ]   [  0     0     1.00  ] [ Harry ]
[ david   ]   [  0     0     1.00  ]
[ Ed      ]   [  1.00  0     0     ]
[ James   ]   [  1.00  0     0     ]
[ Jason   ]   [  1.00  1.00  0     ]
[ John    ]   [  0     1.00  0     ]
[ lena    ]   [  0     1.00  0     ]
[ Liz     ]   [  1.00  0     0     ]
[ Mary    ]   [  1.00  0     0     ]
[ nathan  ]   [  1.00  1.00  1.00  ]
[ sam     ]   [  0     0     1.00  ]
[ smithie ]   [  0     0     1.00  ]

sa: matrix[links-to]
[ url a ] = [  0     0     1.00  ] [ url 1 ]
[ url b ]   [  1.00  1.00  0     ] [ url 2 ]
[ url c ]   [  0     0     1.00  ] [ url 3 ]
[ url d ]   [  0     0     1.00  ]
[ url e ]   [  1.00  0     0     ]
[ url f ]   [  1.00  0     0     ]
[ url g ]   [  1.00  1.00  0     ]
[ url h ]   [  0     1.00  0     ]
[ url i ]   [  0     1.00  0     ]
[ url j ]   [  1.00  0     0     ]
[ url k ]   [  1.00  0     0     ]
[ url l ]   [  1.00  1.00  1.00  ]
[ url m ]   [  0     0     1.00  ]
[ url n ]   [  0     0     1.00  ]
I personally think that is cool, and kind of pretty.
Also has the consequence that if you map out the neural structure of some set of neurons then it is very hard to go from structure alone to finding the meaning of that structure.

matrices in sw format

OK. Continuing on from the matrix theme in the last post, a couple of simple matrices in sw and matrix representations.

In BKO we simply have:
-- NB: we drop/ignore terms from superpositions that have coeff == 0.
M1 |x1> => 3|y2>  
M1 |x2> => 7|y1> + 6|y2>
M1 |x3> => |y1> + 4|y2>
M1 |x4> => |y1> 
M1 |x5> => 6|y1> + 4|y2>
M1 |x6> => 4|y1> + 8|y2>
M1 |x7> => |y1> + 2|y2>

M2 |y1> => 6|z1> + 2|z2> + 7|z3> + 9|z4> + 5|z5>
M2 |y2> => 3|z2> + 4|z3> + |z5>
Now as matrices:
sa: matrix[M1]
[ y1 ] = [  0     7.00  1.00  1.00  6.00  4.00  1.00  ] [ x1 ]
[ y2 ]   [  3.00  6.00  4.00  0     4.00  8.00  2.00  ] [ x2 ]
                                                        [ x3 ]
                                                        [ x4 ]
                                                        [ x5 ]
                                                        [ x6 ]
                                                        [ x7 ]

sa: matrix[M2]
[ z1 ] = [  6.00  0     ] [ y1 ]
[ z2 ]   [  2.00  3.00  ] [ y2 ]
[ z3 ]   [  7.00  4.00  ]
[ z4 ]   [  9.00  0     ]
[ z5 ]   [  5.00  1.00  ]

sa: matrix[M2,M1]
[ z1 ] = [  6.00  0     ] [  0     7.00  1.00  1.00  6.00  4.00  1.00  ] [ x1 ]
[ z2 ]   [  2.00  3.00  ] [  3.00  6.00  4.00  0     4.00  8.00  2.00  ] [ x2 ]
[ z3 ]   [  7.00  4.00  ]                                                [ x3 ]
[ z4 ]   [  9.00  0     ]                                                [ x4 ]
[ z5 ]   [  5.00  1.00  ]                                                [ x5 ]
                                                                         [ x6 ]
                                                                         [ x7 ]

sa: merged-matrix[M2,M1]
[ z1 ] = [  0      42.00  6.00   6.00  36.00  24.00  6.00   ] [ x1 ]
[ z2 ]   [  9.00   32.00  14.00  2.00  24.00  32.00  8.00   ] [ x2 ]
[ z3 ]   [  12.00  73.00  23.00  7.00  58.00  60.00  15.00  ] [ x3 ]
[ z4 ]   [  0      63.00  9.00   9.00  54.00  36.00  9.00   ] [ x4 ]
[ z5 ]   [  3.00   41.00  9.00   5.00  34.00  28.00  7.00   ] [ x5 ]
                                                              [ x6 ]
                                                              [ x7 ]
So I guess the take home point is that it is easy to map back and forth between sw/bko and matrices. Yeah, matrices are pretty to look at, but in general I find it easier to work with BKO. And certainly in the case of large sparse matrices, sw format is far more efficient!

Update:
Unlike standard matrices, where the input and output vectors usually aren't associated with labels, here they are.
eg, if we consider:
sa: matrix[M1,M2]
[  ] = [  0  0  0  0  0  ] [  6.00  0     ] [ y1 ]
                           [  2.00  3.00  ] [ y2 ]
                           [  7.00  4.00  ]
                           [  9.00  0     ]
                           [  5.00  1.00  ]
This is because the output from the M2 matrix is [z1,z2,z3,z4,z5] which has no knowledge of the M1 operator/matrix, which has only been defined with respect to |xn>.

The other thing to note is that M2 M1 does the right then when applied to ket/superpositions.
eg:
sa: M2 M1 |x3>
6.000|z1> + 14.000|z2> + 23.000|z3> + 9.000|z4> + 9.000|z5>

sa: M2 M1 |x7>
6.000|z1> + 8.000|z2> + 15.000|z3> + 9.000|z4> + 7.000|z5>
which is exactly what we expect when we look at the merged M2 M1 matrix above.

Update: I think I should add a little more here. First up, the point I was trying to make above is that in standard maths, matrices are "anonymous". They don't care about the names of the incoming vector basis elements, and the out-going vector base element names. But in BKO there are no anonymous matrices at all! They are all defined with respect to kets. I guess in the neural network context this makes sense. Matrices are defined with respect to particular neurons (ie, kets), rather than some unnamed collection of input neurons. This also means if you apply a BKO matrix to the wrong superposition, then you get the empty superposition as a result. eg, the matrix[M1,M2] example above.

Also, I want to do another matrix example:
Say we have this matrix:
y = M x
[ y1 ]   [ 0 1 1 0 ] [ x1 ]
[ y2 ] = [ 4 0 2 3 ] [ x2 ]
[ y3 ]   [ 2 1 4 4 ] [ x3 ]
                     [ x4 ]
In BKO this is (yeah, in this example I included the 0 coeff kets too):
M |x1> => 0|y1> + 4|y2> + 2|y3>
M |x2> => |y1> + 0|y2> + |y3>
M |x3> => |y1> + 2|y2> + 4|y3>
M |x4> => 0|y1> + 3|y2> + 4|y3>
Now, let's show an example of matrix multiplication with a vector. Let's say x = (1,1,1,1), then we have:
sa: M (|x1> + |x2> + |x3> + |x4>)
2|y1> + 9|y2> + 11|y3>
ie, we interpret the resulting superposition as: y = (2,9,11).
Next, say x = (9,3,0,4):
sa: M (9|x1> + 3|x2> + 0|x3> + 4|x4>)
3|y1> + 48|y2> + 37|y3>
ie, y = (3,48,37)

simple network in sw format

One interpretation of the BKO scheme is that it is a general notation to represent networks. Indeed, it is almost trivial to define networks in BKO. Here is a simple network, but extends easily:

Here is the BKO, and we have just decided to propagate the network using the "O" operator. You can choose anything really.
O |a1> => |a2>
O |a2> => |a3>
O |a3> => |a4>
O |a4> => |a5>
O |a5> => |a6>
O |a6> => |a7>
O |a7> => |a8>
O |a8> => |a9>
O |a9> => |a10>
O |a10> => |a1> + |b1>

O |b1> => |b2>
O |b2> => |b3>
O |b3> => |b4>
O |b4> => |b5>
O |b5> => |b6>
O |b6> => |b7>
O |b7> => |b1>
And being a network, we have a corresponding matrix representation:
sa: matrix[O]
[ a1  ] = [  0  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0  ] [ a1  ]
[ a2  ]   [  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  ] [ a2  ]
[ a3  ]   [  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  ] [ a3  ]
[ a4  ]   [  0  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  ] [ a4  ]
[ a5  ]   [  0  0  0  1  0  0  0  0  0  0  0  0  0  0  0  0  0  ] [ a5  ]
[ a6  ]   [  0  0  0  0  1  0  0  0  0  0  0  0  0  0  0  0  0  ] [ a6  ]
[ a7  ]   [  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  0  0  ] [ a7  ]
[ a8  ]   [  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  0  ] [ a8  ]
[ a9  ]   [  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  ] [ a9  ]
[ a10 ]   [  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  ] [ a10 ]
[ b1  ]   [  0  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  1  ] [ b1  ]
[ b2  ]   [  0  0  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  ] [ b2  ]
[ b3  ]   [  0  0  0  0  0  0  0  0  0  0  0  1  0  0  0  0  0  ] [ b3  ]
[ b4  ]   [  0  0  0  0  0  0  0  0  0  0  0  0  1  0  0  0  0  ] [ b4  ]
[ b5  ]   [  0  0  0  0  0  0  0  0  0  0  0  0  0  1  0  0  0  ] [ b5  ]
[ b6  ]   [  0  0  0  0  0  0  0  0  0  0  0  0  0  0  1  0  0  ] [ b6  ]
[ b7  ]   [  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  1  0  ] [ b7  ]
Now we have a network, we can propagate around it by applying the O operator repeatedly, which corresponds directly to the idea of applying the O matrix to a vector, where superpositions can be considered to be "sparse representations" of vectors -- we drop/ignore all elements that have coeff = 0. Let's start with 1 seed node, and step around. OK. I picked |a5> as the starting node:
sa: O |a5>
|a6>

sa: O O |a5>
|a7>

-- yeah. I got lazy, and dropped to the exponential notation (as you should!):
sa: O^3 |a5>
|a8>

sa: O^4 |a5>
|a9>

sa: O^5 |a5>
|a10>

sa: O^6 |a5>
|a1> + |b1>

sa: O^7 |a5>
|a2> + |b2>

sa: O^8 |a5>
|a3> + |b3>
And so on.
Now, let's pick more than one seed node, and add in some non {0,1} coeffs:
sa: O (|a2> + 7|a5> + 30 |a10> + 3.14|b5>)
|a3> + 7.000|a6> + 30.000|a1> + 30.000|b1> + 3.140|b6>

sa: O^2 (|a2> + 7|a5> + 30 |a10> + 3.14|b5>)
|a4> + 7.000|a7> + 30.000|a2> + 30.000|b2> + 3.140|b7>

sa: O^3 (|a2> + 7|a5> + 30 |a10> + 3.14|b5>)
|a5> + 7.000|a8> + 30.000|a3> + 30.000|b3> + 3.140|b1>

sa: O^4 (|a2> + 7|a5> + 30 |a10> + 3.14|b5>)
|a6> + 7.000|a9> + 30.000|a4> + 30.000|b4> + 3.140|b2>

sa: O^5 (|a2> + 7|a5> + 30 |a10> + 3.14|b5>)
|a7> + 7.000|a10> + 30.000|a5> + 30.000|b5> + 3.140|b3>

sa: O^6 (|a2> + 7|a5> + 30 |a10> + 3.14|b5>)
|a8> + 7.000|a1> + 7.000|b1> + 30.000|a6> + 30.000|b6> + 3.140|b4>

sa: O^7 (|a2> + 7|a5> + 30 |a10> + 3.14|b5>)
|a9> + 7.000|a2> + 7.000|b2> + 30.000|a7> + 30.000|b7> + 3.140|b5> 
One advantage of the BKO notation is that it is trivial to define and redefine our network (much easier than manually entering elements into a matrix). How about we change a couple of rules:
sa: O |a4> => |a5> + 300 |b5>
sa: O |b2> => |b3> + 0.5 |a2>
Now, check out the new matrix:
sa: matrix[O]
[ a1  ] = [  0  0  0  0    0  0  0  0  0  1  0  0    0  0  0  0  0  ] [ a1  ]
[ a2  ]   [  1  0  0  0    0  0  0  0  0  0  0  0.5  0  0  0  0  0  ] [ a2  ]
[ a3  ]   [  0  1  0  0    0  0  0  0  0  0  0  0    0  0  0  0  0  ] [ a3  ]
[ a4  ]   [  0  0  1  0    0  0  0  0  0  0  0  0    0  0  0  0  0  ] [ a4  ]
[ a5  ]   [  0  0  0  1    0  0  0  0  0  0  0  0    0  0  0  0  0  ] [ a5  ]
[ a6  ]   [  0  0  0  0    1  0  0  0  0  0  0  0    0  0  0  0  0  ] [ a6  ]
[ a7  ]   [  0  0  0  0    0  1  0  0  0  0  0  0    0  0  0  0  0  ] [ a7  ]
[ a8  ]   [  0  0  0  0    0  0  1  0  0  0  0  0    0  0  0  0  0  ] [ a8  ]
[ a9  ]   [  0  0  0  0    0  0  0  1  0  0  0  0    0  0  0  0  0  ] [ a9  ]
[ a10 ]   [  0  0  0  0    0  0  0  0  1  0  0  0    0  0  0  0  0  ] [ a10 ]
[ b1  ]   [  0  0  0  0    0  0  0  0  0  1  0  0    0  0  0  0  1  ] [ b1  ]
[ b2  ]   [  0  0  0  0    0  0  0  0  0  0  1  0    0  0  0  0  0  ] [ b2  ]
[ b3  ]   [  0  0  0  0    0  0  0  0  0  0  0  1    0  0  0  0  0  ] [ b3  ]
[ b4  ]   [  0  0  0  0    0  0  0  0  0  0  0  0    1  0  0  0  0  ] [ b4  ]
[ b5  ]   [  0  0  0  300  0  0  0  0  0  0  0  0    0  1  0  0  0  ] [ b5  ]
[ b6  ]   [  0  0  0  0    0  0  0  0  0  0  0  0    0  0  1  0  0  ] [ b6  ]
[ b7  ]   [  0  0  0  0    0  0  0  0  0  0  0  0    0  0  0  1  0  ] [ b7  ]
And our code can also spit out a matrix that has been applied a few times, eg 10 times:
sa: merged-matrix[O,O,O,O,O,O,O,O,O,O]
[ a1  ] = [  1    0    0      0      0    0    0    0    0    0      0    0.5  0    0    0    0    0    ] [ a1  ]
[ a2  ]   [  0    1    0.5    0      0    0    0    0    0    150.5  75   0    0    0    0    0    0.5  ] [ a2  ]
[ a3  ]   [  150  0    1      0.5    0    0    0    0    0    0      0.5  75   0    0    0    0    0    ] [ a3  ]
[ a4  ]   [  0    150  0      1      0.5  0    0    0    0    0      0    0.5  0    0    0    0    0    ] [ a4  ]
[ a5  ]   [  0    0    150    0      1    0.5  0    0    0    0      0    0    0.5  0    0    0    0    ] [ a5  ]
[ a6  ]   [  0    0    0      150    0    1    0.5  0    0    0      0    0    0    0.5  0    0    0    ] [ a6  ]
[ a7  ]   [  0    0    0      0      0    0    1    0.5  0    0      0    0    0    0    0.5  0    0    ] [ a7  ]
[ a8  ]   [  0    0    0      0      0    0    0    1    0.5  0      0    0    0    0    0    0.5  0    ] [ a8  ]
[ a9  ]   [  0    0    0      0      0    0    0    0    1    0.5    0    0    0    0    0    0    0.5  ] [ a9  ]
[ a10 ]   [  0    0    0      0      0    0    0    0    0    1      0.5  0    0    0    0    0    0    ] [ a10 ]
[ b1  ]   [  1    0    0      0      0    0    0    301  150  0      0    0.5  0    0    1    150  0    ] [ b1  ]
[ b2  ]   [  0    1    0      0      0    0    0    0    301  150    0    0    0    0    0    1    150  ] [ b2  ]
[ b3  ]   [  0    0    1      0      0    0    0    0    0    301    150  0    0    0    0    0    1    ] [ b3  ]
[ b4  ]   [  300  0    0      1      0    0    0    0    0    0      1    150  0    0    0    0    0    ] [ b4  ]
[ b5  ]   [  0    300  45000  0      301  150  0    0    0    0      0    1    150  0    0    0    0    ] [ b5  ]
[ b6  ]   [  0    0    300    45000  0    301  150  0    0    0      0    0    1    150  0    0    0    ] [ b6  ]
[ b7  ]   [  0    0    0      300    0    0    301  150  0    0      0    0    0    1    150  0    0    ] [ b7  ]
Now, it would be nice if we could just do: merged-matrix[O^10] or something, but my code can't currently do that. Shouldn't be too hard to add if I get the inclination.

That's it for this post. I think some more matrices in the next post or two.

diversion: the tidy language underneath

So, the BKO scheme requirement that everything is a ket or a superposition means that some things are (significantly) messier/uglier than in more general purpose languages. So today a brief comparison with the BKO versus a theoretical tidier language. Note that the simplifying assumption of this tidy language is that objects are either numbers or strings, whereas in BKO objects are "numbers associated with strings".

Anyway, just some examples, and then we are done:
Factorial:
fact |0> => |1>
n-1 |*> #=> arithmetic(|_self>,|->,|1>)
fact |*> #=> arithmetic( |_self>, |*>, fact n-1 |_self>)
becomes:
fact 0 = 1
n-1 * = _self - 1
fact * = _self * fact n-1 _self
Fibonacci:
fib |0> => |0>
fib |1> => |1>
n-1 |*> #=> arithmetic(|_self>,|->,|1>)
n-2 |*> #=> arithmetic(|_self>,|->,|2>)
fib |*> #=> arithmetic( fib n-1 |_self>, |+>, fib n-2 |_self>)
fib-ratio |*> #=> arithmetic( fib |_self> , |/>, fib n-1 |_self> )
becomes:
fib 0 = 0
fib 1 = 1
n-1 * = _self - 1
n-2 * = _self - 2
fib * = fib n-1 _self + fib n-2 _self
fib-ratio * = fib _self / fib n-1 _self
Random greet:
hello |*> #=> merge-labels(|Hello, > + |_self> + |!>)
hey |*> #=> merge-labels(|Hey Ho! > + |_self> + |.>)
wat-up |*> #=> merge-labels (|Wat up my homie! > + |_self> + | right?>)
greetings |*> #=> merge-labels(|Greetings fine Sir. I believe they call you > + |_self> + |.>)
howdy |*> => |Howdy partner!>
good-morning |*> #=> merge-labels(|Good morning > + |_self> + |.>)
gday |*> #=> merge-labels(|G'day > + |_self> + |.>)
random-greet |*> #=> pick-elt ( hello |_self> + hey |_self> + wat-up |_self> + greetings |_self> + howdy |_self> + good-morning |_self> + gday |_self>)
friends-list |*> #=> extract-value list-to-words extract-value friends |_self>
becomes:
hello * = "Hello, ${_self}!"
hey * = "Hey Ho! ${_self}."
wat-up * = "Wat up my homie! ${_self} right?"
greetings * = "Greetings fine Sir. I believe they call you ${_self}."
howdy * = "Howdy partner!"
good-morning * = "Good morning ${_self}."
gday * = "G'day ${_self}."
random-greet * = pick-elt [ hello _self, hey _self, wat-up _self, greetings _self, howdy _self, good-morning _self, gday _self]
friends-list * = extract-value list-to-words extract-value friends _self 
And I guess that is about all I have to say about that.

Wednesday, 3 December 2014

factorial and Fibonacci in BKO

Last post I defined arithmetic. So here a couple of examples:
Factorial:
|context> => |context: factorial>
fact |0> => |1>
n-1 |*> #=> arithmetic(|_self>,|->,|1>)
fact |*> #=> arithmetic( |_self>, |*>, fact n-1 |_self>)

eg:
sa: fact |5>
|120>

Fibonacci:
|context> => |context: Fibonacci>
fib |0> => |0>
fib |1> => |1>
n-1 |*> #=> arithmetic(|_self>,|->,|1>)
n-2 |*> #=> arithmetic(|_self>,|->,|2>)
fib |*> #=> arithmetic( fib n-1 |_self>, |+>, fib n-2 |_self>)
fib-ratio |*> #=> arithmetic( fib |_self> , |/>, fib n-1 |_self> )

eg:
sa: fib |10>
|55>

sa: fib |11>
|89>

sa: fib |20>
|6765>

sa: fib |21>
|10946>

sa: fib-ratio |30>
|1.6180339887482036>
-- NB: if we have only defined fib |0> and fib |1> the debugging info for fib-ratio |30> is insane!
So a massive speed up is to do these first:
fib |10> => fib |10>
fib |11> => fib |11>
fib |20> => fib |20>
fib |21> => fib |21>
-- NB: these work by making use of label descent.
-- and are an example of "memoization".

Next thing to observe is that fib/fact are linear and are applied separately to all kets in the superposition:
sa: fib (|10> + |11> + |12> + |13> + |14> + |15>)
|55> + |89> + |144> + |233> + |377> + |610>

sa: fact (|0> + |1> + |2> + |3> + |4> + |5> + |6> + |7>)
2.000|1> + |2> + |6> + |24> + |120> + |720> + |5040>
And that's it for now.
I guess I will also mention that I had not intended BKO to handle recursion, it just emerged naturally after implementing stored rules.

Update: First, it is time I explained these:
fib |10> => fib |10>
what is going on is that fib |10> on the right hand side is being computed. Then the result is being stored at "fib |10>". So next time you ask what is "fib |10>" it does a simple look-up, and not a full computation. The reason is that a simple look up has higher precedence than the general "fib |*>" rule (which is because of how we implemented label-descent).

Next, a quick example of the magic of tables:
sa: table[number,fact,fib] range(|1>,|11>)
+--------+----------+-----+
| number | fact     | fib |
+--------+----------+-----+
| 1      | 1        | 1   |
| 2      | 2        | 1   |
| 3      | 6        | 2   |
| 4      | 24       | 3   |
| 5      | 120      | 5   |
| 6      | 720      | 8   |
| 7      | 5040     | 13  |
| 8      | 40320    | 21  |
| 9      | 362880   | 34  |
| 10     | 3628800  | 55  |
| 11     | 39916800 | 89  |
+--------+----------+-----+

arithmetic

Next up, arithmetic. Now, note BKO is not designed to be a general purpose programming language. There are zillions of those! BKO is aimed at the knowledge-representation/AI niche.

The point is, we can do arithmetic, but it is ugly. The python now, and a couple of examples (fact and fib) in the next post. Not super happy with this code, but it does the job, and it was from way back when I was still in the early stages of learning python.
# the arithmetic function
# eg: arithmetic(|number: 3>,|symbol: +>,|number: 8>)
#
def arithmetic(x,operator,y):
  x_label = x if type(x) == str else x.the_label()
  op_label = operator if type(operator) == str else operator.the_label()
  y_label = y if type(y) == str else y.the_label()

  cat1, v1 = extract_category_value(x_label)
  name, op = extract_category_value(op_label)
  cat2, v2 = extract_category_value(y_label)
  if cat1 != cat2 or op not in ['+','-','*','/','%','^']:
    return ket("")
  try:
    x = int(v1)
    y = int(v2)
  except ValueError:
    try:
      x = float(v1)
      y = float(v2)
    except ValueError:
      return ket("")
  label = ""
  if len(cat1) > 0:
    label = cat1 + ": "      
  if op == '+':
    return ket(label + str(x + y))
  elif op == '-':
    return ket(label + str(x - y))
  elif op == '*':
    return ket(label + str(x * y))
  elif op == '/':
    if y == 0:         # prevent div by zero
      return ket("",0)
    return ket(label + str(x / y))
  elif op == '%':
    return ket(label + str(x % y))
  elif op == '^':
    return ket(label + str(x ** y))
  else:
    return ket("")   # presumably this should never be reached. 
I guess a couple of things to note:
1) the |> wrapping around everything is a tad ugly in this case. But we must stick to every object being either a ket or a superposition, otherwise we break our model.
2) heh, the "amplification factor" of this function versus the python "answer = a*b" is huge!

And I guess that is it. I'll put it to use in the next post.

Update: note that the arithmetic function returns |> if the categories/data-types do not match. This is to prevent errors where you mix data-types. eg, say adding pounds and kilos.

Update: the standard solution to arithmetic on kets you are not 100% sure of the data-type is something like this:
arithmetic(to-km |x>,|+>,to-km |y>)
which gives an answer in km, assuming |x> and |y> support the to-km operator, independent of the actual type of x and y.

random greetings in BKO

This time, an example of random greetings. Makes use of general rules, stored rules, merge-labels(), the self ket and pick-elt.

Let's define our greetings:
|context> => |context: greetings play>

hello |*> #=> merge-labels(|Hello, > + |_self> + |!>)
hey |*> #=> merge-labels(|Hey Ho! > + |_self> + |.>)
wat-up |*> #=> merge-labels (|Wat up my homie! > + |_self> + | right?>)
greetings |*> #=> merge-labels(|Greetings fine Sir. I believe they call you > + |_self> + |.>)
howdy |*> => |Howdy partner!>
good-morning |*> #=> merge-labels(|Good morning > + |_self> + |.>)
gday |*> #=> merge-labels(|G'day > + |_self> + |.>)
random-greet |*> #=> pick-elt ( hello |_self> + hey |_self> + wat-up |_self> + greetings |_self> + howdy |_self> + good-morning |_self> + gday |_self>)
Now, let's put it to use:
sa: random-greet |Fred>
|Greetings fine Sir. I believe they call you Fred.>

sa: random-greet |John>
|Good morning John.>

sa: random-greet |Emma>
|Hello, Emma!>

-- define who we are talking with:
sa: |you> => |Tim>
-- greet them:
sa: random-greet "" |you>
|G'day Tim.>
Next, with a little work, we can greet a list of people.
-- define this beasty first:
sa: friends-list |*> #=> extract-value list-to-words friends |_self>

-- define some friends:
sa: friends |Sam> => |Charlie> + |George> + |Emma> + |Jack> + |Robert> + |Frank> + |Julie>
sa: friends |Emma> => |Liz> + |Bob>

-- check the output of the friends-list operator:
sa: friends-list |Sam>
|Charlie, George, Emma, Jack, Robert, Frank and Julie>

sa: friends-list |Emma>
|Liz and Bob>
-- notice that "list-to-words" has converted a superposition of names into an English like list of names.
-- eg: 
sa: list-to-words (|a> + |b> + |c> + |d>)
|text: a, b, c and d>

-- now, let's greet them:
sa: random-greet friends-list |Emma>
|Wat up my homie! Liz and Bob right?>

sa: random-greet friends-list |Sam>
|G'day Charlie, George, Emma, Jack, Robert, Frank and Julie.>
Anyway, I think that is cool! More to come. Also, a lol at the vast amount of debugging noise this generates! An addition: the above is a good reason why merge-labels() should be built into the parser. One idea is to introduce the _ character to represent merge-labels():
hello |*> #=> |Hello, > _ |_self> _ |!>
hey |*> #=> |Hey Ho! > _ |_self> _ |.>
wat-up |*> #=> |Wat up my homie! > _ |_self> _ | right?>
greetings |*> #=> |Greetings fine Sir. I believe they call you > _ |_self> _ |.>
howdy |*> => |Howdy partner!>
good-morning |*> #=> |Good morning > _ |_self> _ |.>
gday |*> #=> |G'day > _ |_self> _ |.>
This is much cleaner than the merge-labels() version! Indeed, there are other possibilities. I haven't given it much thought yet.
eg: something like this perhaps:
hello |*> #=> insert-self |Hello, ${_self}!>
And then plurals would look like:
plural |word: *> #=> insert-self |${_self}s>
Anyway, some thinking to do.
Update: I'm thinking of going for something like this:
hey |*> #=> insert[|_self>] |Hey ${1}!>
hello-fred-and |*> #=> insert[|Fred>,|_self>] |Hey ${1} and ${2}! How is it going?>
A couple of notes:
1) Everywhere else in the project my numbers start from 1 not the comp-sci convention of 0. So it would break that convention if I used ${0} and ${1} here.
2) The current parser can't handle an operator like insert[] with ket parameters. On the long to-do list!

Update: Wrote an improved version of random-greet. This time the list of greet operators has been separated out. Key changes are these two lines:
|greet list> => |op: hello> + |op: hey> + |op: wat-up> + |op: greetings> + |op: howdy> + |op: good-morning> + |op: gday>
random-greet |*> #=> apply(pick-elt "" |greet list>,|_self>) 
This change has a couple of advantages:
1) we can dynamically change our list of greetings without having to redefine the random-greet operator, instead we now redefine |greet list>. I think this is an improvement.
2) the old version calculated the greetings in full, and then picked from the results. In the new method only the chosen greeting is calculated. So in this case, not a super big win, but if we use a similar technique on a larger example, then it will be a win.

introducing pick-elt

So, we are slowly trying to work towards what humans do. eg, the plurals, the easy definition of close relatives and so on. Today, random greetings. Makes use of merge-labels() and pick-elt. First, let's introduce pick-elt.

The definition of pick-elt is simply, given a superposition randomly return a ket in that superposition. Currently it is unweighted. ie, there is no bias in choice of ket. It is probably useful later to define a weighted pick-elt.

Anyway, a pick-elt example:

sa: pick-elt (|a> + |b> + |c> + |d> + |e> + |f> + |g> + |h>)
|e>
where dot "." in the console means repeat last computation.
sa: .
|a>

sa: .
|c>

sa: .
|h>

sa: .
|g>

So that should be clear enough. Each time it randomly picks kets.

I guess I should also note that it preserves coeffs:
sa: pick-elt (10|a> + 3.141592|pi> + |fred> + 7|cats>)
7.000|cats>
sa: .
7.000|cats>
sa: .
10.000|a>
sa: .
7.000|cats>
sa: .
3.142|pi>

And I suppose you could say pick-elt has a little similarity with the idea of wave-function collapse in QM. Just a little. Indeed, for the QM case you would need a weighted-pick-elt.
eg, Schrodinger's cat in BKO might look like:
sa: is-alive |cat> #=> normalize pick-elt (0.5|yes> + 0.5|no>)
-- ask if the cat is alive?
sa: is-alive |cat>
|no>
sa: .
|no>
sa: .
|yes>

where "normalize" sets the sum of the coeffs of the superposition it is applied to to 1.
eg:
sa: normalize (3|a> + 10|b> + |c>)
0.214|a> + 0.714|b> + 0.071|c>

And that's about it for pick-elt. Very useful all over the place.

Tuesday, 2 December 2014

learning plurals in BKO

Now, between general rules, stored rules, the self ket, and merge-labels() we can now give an example of learning plurals.
-- before we have defined anything:
sa: plural |word: cat>
|>
-- recall in English |> means "I don't know anything about that".

-- define a general rule:
sa: plural |word: *> #=> merge-labels(|_self> + |s>)

-- test it:
sa: plural |word: cat>
|word: cats>

sa: plural |word: dog>
|word: dogs>

-- ok. But what about the irregular forms?
sa: plural |word: mouse>
|word: mouses>

sa: plural |word: foot>
|word: foots>

-- ok. we have a general rule, now define specific rules:
-- learn mouse specific rule:
sa: plural |word: mouse> => |word: mice>

-- learn foot specific rule:
sa: plural |word: foot> => |word: feet>

-- now, try again:
sa: plural |word: mouse>
|word: mice>

sa: plural |word: foot>
|word: feet>
And of course, we can define plurals for other words too.
eg, I suppose:
sa: plural |word: radius> => |word: radii>
Now again. Knowledge representation comes with the idea that there are multiple representations for the same knowledge.
Here we give the matrix representation of the above:
sa: matrix[plural]
[ word: *s    ] = [  1.00  0     0     0     ] [ word: *      ]
[ word: feet  ]   [  0     1.00  0     0     ] [ word: foot   ]
[ word: mice  ]   [  0     0     1.00  0     ] [ word: mouse  ]
[ word: radii ]   [  0     0     0     1.00  ] [ word: radius ]
I guess that is it for now.

the merge-labels() function

Now, so far virtually all of the operators have been literal operators. The only two non-literal operators I have used are "pick-elt" and "apply()".
Today, briefly mention merge-labels().

Does pretty much what it says:
sa: merge-labels(|a> + |b> + |c>)
|abc>
ie, it merges the labels of the kets in the superposition into one ket.
Simple, but turns out to be relatively useful.

There is a hidden problem though!
Kets in superpositions add, so for example:
sa: merge-labels(|a> + |b> + |c> + |a> + |a>)
|abc>
When you might legitimately expect |abcaa>.

We can't currently solve this, but I'm thinking of promoting merge-labels to a built in feature of the parser.
Maybe using _ to separate kets.
eg, something like:
sa: |a> _ |b> _  |c> _ |a> _ |a>
returning:
|abcaa>
That would certainly be much cleaner than the current notation, and solve the superposition bug.

That's it for now. One use of merge-label() in the next post!

the methanol molecule in sw format

Relatively short one this time. Just representing a molecule in sw format. Here, methanol, but the idea should generalize to other molecules easily enough. This is just an example representation. There are probably better ones, depending on what you are trying to do.

|context> => |context: methanol>

molecular-pieces |molecule: methanol> => |methanol: 1> + |methanol: 2> + |methanol: 3> + |methanol: 4> + |methanol: 5> + |methanol: 6>

atom-type |methanol: 1> => |atom: H>
bonds-to |methanol: 1> => |methanol: 4>

atom-type |methanol: 2> => |atom: H>
bonds-to |methanol: 2> => |methanol: 4>

atom-type |methanol: 3> => |atom: H>
bonds-to |methanol: 3> => |methanol: 4>

atom-type |methanol: 4> => |atom: C>
bonds-to |methanol: 4> => |methanol: 1> + |methanol: 2> + |methanol: 3> + |methanol: 5>

atom-type |methanol: 5> => |atom: O>
bonds-to |methanol: 5> => |methanol: 4> + |methanol: 6>

atom-type |methanol: 6> => |atom: H>
bonds-to |methanol: 6> => |methanol: 5>
Not much to say, really. I guess a mildly interesting observation is we can auto-count the atom types:
sa: load methanol.sw
sa: atom-type molecular-pieces |molecule: methanol>
4|atom: H> + |atom: C> + |atom: O>
That's it for now!

Update: now with the magic of tables:
sa: load methanol.sw
sa: table[piece,atom-type,bonds-to] molecular-pieces |molecule: methanol>
+-------+-----------+------------+
| piece | atom-type | bonds-to   |
+-------+-----------+------------+
| 1     | H         | 4          |
| 2     | H         | 4          |
| 3     | H         | 4          |
| 4     | C         | 1, 2, 3, 5 |
| 5     | O         | 4, 6       |
| 6     | H         | 5          |
+-------+-----------+------------+
Pretty!

some general people rules

OK. Now we have |_self>, general rules, and stored rules, we can give this example.
Basically a set of rules that apply to all people. I was going to put it in the George example post, but I think it deserves its own post.

Simply:
-- some general rules that apply to all people:
siblings |person: *> #=> brothers |_self> + sisters |_self>
children |person: *> #=> sons |_self> + daughters |_self>
parents |person: *> #=> mother |_self> + father |_self>
uncles |person: *> #=> brothers parents |_self>
aunts |person: *> #=> sisters parents |_self>
aunts-and-uncles |person: *> #=> siblings parents |_self>
cousins |person: *> #=> children siblings parents |_self>
grand-fathers |person: *> #=> father parents |_self>
grand-mothers |person: *> #=> mother parents |_self>
grand-parents |person: *> #=> parents parents |_self>
grand-children |person: *> #=> children children |_self>
great-grand-parents |person: *> #=> parents parents parents |_self>
great-grand-children |person: *> #=> children children children |_self>
immediate-family |person: *> #=> siblings |_self> + parents |_self> + children |_self>
friends-and-family |person: *> #=> friends |_self> + family |_self>
Let's call the above "general-people-rules.sw" and load it in the console:
(note that we haven't defined context, so it happily loads into your current context -- this is often a useful trick)
sa: load general-people-rules.sw

-- now, let's define some knowledge about Fred:
sa: mother |person: Fred> => |person: Judith>
sa: father |person: Fred> => |person: Frank>
sa: sisters |person: Fred> => |person: Liz> + |person: Emma> + |person: Jane>
OK. So what?
Well, using the general rules, we can now answer who Fred's parents and siblings are:
(without having to specify them manually. mother/father/sisters/brothers is sufficient)
sa: parents |person: Fred>
|person: Judith> + |person: Frank>

sa: siblings |person: Fred>
|person: Liz> + |person: Emma> + |person: Jane>

-- OK. Learn Fred has a brother Jack:
sa: brothers |person: Fred> => |person: Jack>

-- now we know that, ask again who Fred's siblings are:
sa: siblings |person: Fred>
|person: Jack> + |person: Liz> + |person: Emma> + |person: Jane>

-- now, how about Fred's immediate-family?
sa: immediate-family |person: Fred>
|person: Jack> + |person: Liz> + |person: Emma> + |person: Jane> + |person: Judith> + |person: Frank>
Also, a LOL. The console spat out a page of debugging info just to provide that last example. So what I haven't told you is that currently the console is very noisy, and I have been chomping out the debugging info by hand. Eventually when I'm 100% happy with everything I guess I will switch off debugging. Probably also a hint that my code is far more inefficient than it needs to be!

So there you have it!
The self ket, "person: *" general rules, and stored rules, all in one example.
And the "person: *" thing is one reason why categories/data-types are often useful to use. We can define general rules that apply to a category of objects, rather than to all objects.

Also, I guess in other projects, the above might be called "an inference engine". I mean, yeah, sort of. But BKO is in some sense more direct, while most inference engines try to use chains of logic.

More to come!

a fictional George in sw format

Let's define what we know about a fictional character we call George.
Really is quite simple.
|context> => |context: George>
source |context: George> => |sw-url: http://semantic-db.org/sw-examples/new-george.sw>

-- George is just some fictional character

|person: George> => |word: george>
age |person: George> => |age: 29>
dob |person: George> => |date: 1984-05-23>
hair-colour |person: George> => |hair-colour: brown>
eye-colour |person: George> => |eye-colour: blue>
gender |person: George> => |gender: male>
height |person: George> => |height: cm: 176>
wife |person: George> => |person: Beth>
occupation |person: George> => |occupation: car salesman>
friends |person: George> => |person: Fred> + |person: Jane> + |person: Liz> + |person: Andrew>
mother |person: George> => |person: Sarah>
father |person: George> => |person: David>
sisters |person: George> => |person: Emily>
brothers |person: George> => |person: Frank> + |person: Tim> + |person: Sam>
email |person: George> => |email: george.douglas@gmail.com>
education |person: George> => |education: high-school> 
My favourite thing about this example is that it shows we can describe all types of "molecules of knowledge" using 1 line, and all in the format:
OP KET => SUPERPOSITION

One advantage of the 1 line thing is that when we have large sw files, it is trivial to grep down to the rules of interest. I will give an example of that in the future, probably using the IMDB data I have in sw format. Indeed, there is not a single construct in BKO that is multi-line. I think this gives it some power, eg, being able to grep, and easier to parse.

Fine, let's give a simple example:
$ grep "person: Jane" blog-george.sw
friends |person: George> => |person: Fred> + |person: Jane> + |person: Liz> + |person: Andrew>
Now, let's load it up and give the pretty-print display:
sa: load blog-george.sw
sa: display
  context: George

  context: George
  supported-ops: op: source
         source: sw-url: http://semantic-db.org/sw-examples/new-george.sw

  person: George
  supported-ops: op: , op: age, op: dob, op: hair-colour, op: eye-colour, op: gender, op: height, op: wife, op: occupation, op: friends, op: mother, op: father, op: sisters, op: brothers, op: email, op: education
               : word: george
            age: age: 29
            dob: date: 1984-05-23
    hair-colour: hair-colour: brown
     eye-colour: eye-colour: blue
         gender: gender: male
         height: height: cm: 176
           wife: person: Beth
     occupation: occupation: car salesman
        friends: person: Fred, person: Jane, person: Liz, person: Andrew
         mother: person: Sarah
         father: person: David
        sisters: person: Emily
       brothers: person: Frank, person: Tim, person: Sam
          email: email: george.douglas@gmail.com
      education: education: high-school
And that's it for now. I was going to put the general people rules here too, but now I will put them in the next post.

Update: now with the magic of tables:
sa: load blog-george.sw
sa: George |*> #=> apply(|_self>,|person: George>)
sa: table[op,George] supported-ops |person: George>
+-------------+--------------------------+
| op          | George                   |
+-------------+--------------------------+
|             | george                   |
| age         | 29                       |
| dob         | 1984-05-23               |
| hair-colour | brown                    |
| eye-colour  | blue                     |
| gender      | male                     |
| height      | 176                      |
| wife        | Beth                     |
| occupation  | car salesman             |
| friends     | Fred, Jane, Liz, Andrew  |
| mother      | Sarah                    |
| father      | David                    |
| sisters     | Emily                    |
| brothers    | Frank, Tim, Sam          |
| email       | george.douglas@gmail.com |
| education   | high-school              |
+-------------+--------------------------+

the |_self> ket

Next feature I want to mention is the |_self> ket.
I use it all the time, and hopefully is not too hard to describe.

Recall my standard learn rule:
OP KET => SUPERPOSITION
well, simply enough, if |_self> is mentioned in the SUPERPOSITION (on the right), then substitute in the value KET.

Simple example I suppose:
sa: op |x> => 100 |_self>
sa: dump
----------------------------------------
|context> => |context: sw console>

op |x> => 100.000|x>
----------------------------------------
See? We have 100 |x> on the right of our learn-rule, not 100 |_self>
And that's about it. Nothing complicated about it at all.

More to come!

label descent

Here is a little beasty that adds a lot of power to BKO.
And it is usually used in conjunction with stored_rules.

The idea is, if you are trying to do:
context.recall(a,b), and no match is found with the specific value of b, try more and more general rules.

Here is some of the python:
def label_descent(x):
  print("x:",x)
  result = [x]
  if x == "*":
    return result
  if x.endswith(": *"):
    x = x[:-3]
  while True:
    try:
      x,null = x.rsplit(": ",1)
      result.append(x + ": *")
    except:
      result.append("*")
      return result

eg, if you feed in this label "a: b: c: d: fred", it returns these trail labels:
a: b: c: d: fred
a: b: c: d: *
a: b: c: *
a: b: *
a: *
*
So given something like this:
op |a: b: c>
if that has no match, then the code next tries:
op |a: b: *>
if that has no match, then the code next tries:
op |a: *>
if that has no match, then the code next tries:
op |*>
if that has no match, then return |>.

Where we consider:
op |a: b: c>
to be a more specific rule than the more general rule:
op |a: b: *>
which is more specific than say:
op |*>

Anyway, the key code in context.recall() is (though without knowing the details of the context class it probably doesn't make a lot of sense):
    match = False
    for trial_label in label_descent(label):
      if trial_label in self.known_kets:
        if op in self.rules[trial_label]:
          rule = self.rules[trial_label][op]
          match = True
          break
    if not match:
      print("recall not found")               
      rule = ket("",0)
Take home message, this thing is seriously powerful, and useful. That's it for now.

current weather in Adelaide in sw format

So a fictional sw file that represents the current weather in my city, Adelaide.
Data is from the Bureau of Meteorology website.
|context> => |context: BOM: Adelaide: current-weather>
-- fictional source. Though maybe one day something like this will be real? 
source |context: BOM: Adelaide: current-weather> => |sw-url: http://www.bom.gov.au/current-adelaide.sw>

description |context: BOM: Adelaide: current-weather> => |text: "Morning shower or two">
date |context: BOM: Adelaide: current-weather> => |date: 2/12/2014>
time  |context: BOM: Adelaide: current-weather> => |time: 5:15pm>

current |temperature> => |C: 25.2>
max |temperature> => |C: 29>
min |temperature> => |C: 20>

direction |wind> => |direction: south>
speed |wind> => |km/h: 6>

|rain> => |mm: 0.0>
Indeed, structurally this is quite clean. I imagine a script working with a back-end data-base could produce files like this very easily.
The harder component is coming up with applications for this information, once we have it in sw format.

Monday, 1 December 2014

add_learn and stored_rules

Next, a couple of additions to the standard learn rule:
OP KET => SUPERPOSITION

First one is just a notational short-cut for something that is common.
Say you have a list of objects (say friends) and you want to append one or more on the end of that list.

Currently, you would have to do:
sa: friends |Fred> => |Mary> + |Liz>
sa: friends |Fred> => friends |Fred> + |Sam> + |Rob> + |Emma>

But I am very lazy, and like to abbreviate common constructs.
And hence, now we can do:
sa: friends |Fred> => |Mary> + |Liz>
sa: friends |Fred> +=> |Sam> + |Rob> + |Emma>
Note the "+=>" in there.

A couple of properties though:
1) it ignores elements if they are already in the list.
eg:
friends |Fred> +=> |Liz>
leaves the friends list unmodified.
2) it (currently) ignores elements even if they have a different coeff.
eg:
|shopping list> => 2|apples> + 5 |oranges>
|shopping list> +=> 7 |apples> -- this line does not affect our shopping list.
Now, I'm just saying that is how it currently works. I might change this in the future.

So all in all, add-learn "+=>" is not a super big win, but useful every now and then.
And perhaps a more appropriate name is "append learn", but too late now!

Next is stored rules. They have notation:
OP KET #=> SUPERPOSITION
and are seriously useful!

Standard "=>" is evaluated at definition time.
"#=>" is evaluated at recall time (and we call them stored_rules).
(heh, I guess a mnemonic is # looks like a barrier that stops you evaluating straight away).

I guess an example might be the best way to explain:

-- define an initial value for "a":
sa: |a> => |start value>

-- define r1 and r2:
-- set r1 to current value of a:
sa: |r1> => "" |a>
-- set r2 to invoke-time value of a:
sa: |r2> #=> "" |a>

sa: "" |r1>
|start value>

sa: "" |r2>
|start value>


-- change the value of a:
sa: |a> => |next value>

-- r1 still has original definition:
sa: "" |r1>
|start value>

-- r2 returns the current value of a:
sa: "" |r2>
|next value>

And that is about it. Just know that they are very, very useful!
More to come tomorrow, probably.

Update:
I think it would be useful to give examples to show these map to the underlying python context.learn().
So we have these examples in BKO:
friends |Fred> => |Jack>
friends |Fred> => |Jack> + |Jill>
friends |Fred> +=> |Emma> + |Liz>
siblings |person: *> #=> sisters |_self> + brothers |_self>
And they look like this in python:
-- NB: auto-casts "Jack" to ket("Jack"). Implemented because it saves typing and is neater to read.
context.learn("friends","Fred","Jack")
context.learn("friends","Fred",ket("Jack") + ket("Jill"))
-- NB: "add_learn":
context.add_learn("friends","Fred",ket("Emma") + ket("Liz"))
-- NB: stored_rule():
context.learn("siblings","person: *",stored_rule("sisters |_self> + brothers |_self>"))
Update: Just a teaser: using a big nest of stored rules you should be able to construct some large functions that are only activated at run time. I plan to give an example of this eventually!

train of thought

Now I have explained supported-ops, apply(), and create inverse, I can show you "train of thought".
Way back when I first started this project, it looked like this:
-- simple code to approximately represent the concept of "train of thought"
train-of-thought[|X>,n] ::= [
|ket> => |X>
repeat[n][
  |op> => pick-elt:supported-ops|ket>
  |ket> => pick-elt:apply(|op>,|ket>)
  Output:|ket>
]
return |ket>
]
(yeah, for a while operators were separated by colons, now by spaces) 

Now, in python it looks like:
# train-of-thought[n] some-superposition
# eg: train-of-thought[20] |colour: red>
#
# where n is an int.
def console_train_of_thought(one,context,n):
  try:
    n = int(n)
  except:
    return ket("",0)

  print("context:",context.name)
  print("one:",one)
  print("n:",n)
  X = one.pick_elt()
  print("|X>:",X)
  print()
  result = superposition()

  for k in range(n):
    op = X.apply_op(context,"supported-ops").pick_elt()  #   |op> => pick-elt supported-ops |X>
    X = X.apply_op(context,op).pick_elt()                #   |X> => pick-elt apply(|op>,|X>)
    result.data.append(X)                   
    print(X.display())
  return result                             # return a record of the train-of-thought 
 
I guess in words, start with a ket.
Look up the list of supported-ops.
Pick one (that's what pick-elt does).
Apply that operator to your starting ket.
Pick one element from that resulting superposition.
Loop.

The note is that for it to work well, you first need lots of data, and you need to have run create inverse.
The reason that is important is, if you don't have inverses you rapidly run into |>, and your train stops!
With inverses, it happily goes round in circles.

Let's give an example. Let's use the early US presidents data:
sa: load early-us-presidents.sw
sa: train-of-thought[20] |Washington>
context: sw console
one: |Washington>
n: 20
|X>: |Washington>
|year: 1793>
0.000|>
0.000|>
0.000|>
...
Doh! We forgot to run "create inverse" and our train of thought died.

Let's try again:
sa: create inverse
sa: train-of-thought[20] |Washington>
context: sw console
one: |Washington>
n: 20
|X>: |Washington>

|early US Presidents: _list>
|Adams>
|party: Federalist>
|Adams>
|number: 2>
|Adams>
|person: John Adams>
|US President: John Adams>
|person: John Adams>
|Adams>
|person: John Adams>
|Adams>
|early US Presidents: _list>
|Q Adams>
|early US Presidents: _list>
|Adams>
|party: Federalist>
|Adams>
|early US Presidents: _list>
|Jefferson>
4.000|early US Presidents: _list> + 7.000|Adams> + 2.000|party: Federalist> + |number: 2> + 3.000|person: John Adams> + |US President: John Adams> + |Q Adams> + |Jefferson>
So, it works, but is not super great. We really need a very large data-set for best results.

That's it for now!

inverse

So, one driving idea of the project is to get the computer to do as much learning by itself that it can. If we can automate some learning task, then we should try to do so.

The other idea behind "inverse" is that it seems biologically plausible.
Say a face.
A face links one way to the name of that face.
But it links the other way too. The name of the face (usually) invokes the mental image of that face.
Another example is the child/parent relation.
A child relation maps a mother to her children.
But the inverse-child relation maps a child to the mother.
So maybe we can do that a little here?

Again, a simple example:
-- learn a simple rule:
sa: op |x> => |a> + |b> + |c>

-- create the set of inverses:
sa: create inverse
sa: dump
----------------------------------------
|context> => |context: sw console>

op |x> => |a> + |b> + |c>

inverse-supported-ops |op: op> => |x>

inverse-op |a> => |x>

inverse-op |b> => |x>

inverse-op |c> => |x>

inverse-supported-ops |op: inverse-supported-ops> => |op: op> + |op: inverse-supported-ops> + |op: inverse-op>

inverse-supported-ops |op: inverse-op> => |a> + |b> + |c>
----------------------------------------
Hrmm... that is kind of a mess. Unfortunately, the output after "create inverse" usually is.
But let's try and explain what's going on.
Say we have:
OP KET-1 => SUPERPOSITION
then when we run "create inverse" for all rules (in the current context) we learn:
inverse-OP ket => KET-1
for all ket's in SUPERPOSITION.

Maybe this example will be clearer:
-- learn the children of Mary and James:
sa: child |Mary> => |Tim> + |Liz> + |Jane> + |Rob>
sa: child |James> => |Tim> + |Liz> + |Jane> + |Rob>

sa: create inverse
sa: dump
----------------------------------------
|context> => |context: sw console>

child |Mary> => |Tim> + |Liz> + |Jane> + |Rob>

child |James> => |Tim> + |Liz> + |Jane> + |Rob>

inverse-supported-ops |op: child> => |Mary> + |James>

inverse-child |Tim> => |Mary> + |James>

inverse-child |Liz> => |Mary> + |James>

inverse-child |Jane> => |Mary> + |James>

inverse-child |Rob> => |Mary> + |James>

inverse-supported-ops |op: inverse-supported-ops> => |op: child> + |op: inverse-supported-ops> + |op: inverse-child>

inverse-supported-ops |op: inverse-child> => |Tim> + |Liz> + |Jane> + |Rob>
----------------------------------------
So given just data on who Mary and Jame's children are, it auto-learnt the inverse relation. eg, Tim's parents (ie inverse-child) are Mary and James.

Here is another example, in a different region of knowledge, primes.
Start with this data:
-- load data from file:
sa: load short-list-primes.sw
sa: dump
----------------------------------------
|context> => |context: sw console>

is-prime |2> => |yes>
is-prime |3> => |yes>
is-prime |4> => |no>
is-prime |5> => |yes>
is-prime |6> => |no>
is-prime |7> => |yes>
is-prime |8> => |no>
is-prime |9> => |no>
is-prime |10> => |no>
is-prime |11> => |yes>
is-prime |12> => |no>
is-prime |13> => |yes>
is-prime |14> => |no>
is-prime |15> => |no>
is-prime |16> => |no>
is-prime |17> => |yes>
is-prime |18> => |no>
is-prime |19> => |yes>
is-prime |20> => |no>
----------------------------------------

Now, create inverses:
sa: create inverse
sa: dump
----------------------------------------
|context> => |context: sw console>

is-prime |2> => |yes>
is-prime |3> => |yes>
is-prime |4> => |no>
is-prime |5> => |yes>
is-prime |6> => |no>
is-prime |7> => |yes>
is-prime |8> => |no>
is-prime |9> => |no>
is-prime |10> => |no>
is-prime |11> => |yes>
is-prime |12> => |no>
is-prime |13> => |yes>
is-prime |14> => |no>
is-prime |15> => |no>
is-prime |16> => |no>
is-prime |17> => |yes>
is-prime |18> => |no>
is-prime |19> => |yes>
is-prime |20> => |no>

inverse-supported-ops |op: is-prime> => |2> + |3> + |4> + |5> + |6> + |7> + |8> + |9> + |10> + |11> + |12> + |13> + |14> + |15> + |16> + |17> + |18> + |19> + |20>

inverse-is-prime |yes> => |2> + |3> + |5> + |7> + |11> + |13> + |17> + |19>

inverse-is-prime |no> => |4> + |6> + |8> + |9> + |10> + |12> + |14> + |15> + |16> + |18> + |20>

inverse-supported-ops |op: inverse-supported-ops> => |op: is-prime> + |op: inverse-supported-ops> + |op: inverse-is-prime>

inverse-supported-ops |op: inverse-is-prime> => |yes> + |no>
----------------------------------------
(note, some new-lines chomped from the dump for readability)

Anyway, the coolness is in these two lines:
inverse-is-prime |yes> => |2> + |3> + |5> + |7> + |11> + |13> + |17> + |19>
inverse-is-prime |no> => |4> + |6> + |8> + |9> + |10> + |12> + |14> + |15> + |16> + |18> + |20>

So, given just yes/no on primes for each number, the create inverse automatically spits out a list of primes, and non-primes. I personally think that is quite cool!

Technical note: it would be nice if "create inverse" was idempotent, but every now and then it is not.

More to come!

supported-ops and apply()

So a couple of key components today.
Let's start with "supported-ops".
The idea is for every ket, to keep a record of operators that are supported by that ket.
This BTW is built into the context.learn() code, so we never generate it manually, and (the parser) ignores you if you try.

The convention is simply, use "op: " as the category/data-type for operators.
So, a simple example:
sa: name |Fred> => |Fred Roberts>
sa: height |Fred> => |cm: 176>
-- use "dump exact" else supported-ops lines are left out for readability reasons.
sa: dump exact
----------------------------------------
|context> => |context: sw console>

supported-ops |Fred> => |op: name> + |op: height>
name |Fred> => |Fred Roberts>
height |Fred> => |cm: 176>
----------------------------------------
So we see the "name" operator is called |op: name>
And the "height" operator is called |op: height>

Now, add in a little more knowledge:
sa: dob |Fred> => |date: 17/5/1986>
sa: friends |Fred> => |Mary> + |Liz> + |Tom> + |Frank>
sa: mother |Fred> => |Sarah Roberts>
sa: father |Fred> => |James Roberts>

-- see the supported-ops for Fred:
sa: supported-ops |Fred>
|op: name> + |op: height> + |op: dob> + |op: friends> + |op: mother> + |op: father>

So, what is the use of this thing?
Well, one example usage (there are others) is to make use of it in the apply() function (more on functions in BKO later!)

sa: apply(|op: father> + |op: mother>,|Fred>)
|James Roberts> + |Sarah Roberts>

And here is one way to see everything we know about Fred in one superposition:
sa: apply(supported-ops |Fred>,|Fred>)
|Fred Roberts> + |cm: 176> + |date: 17/5/1986> + |Mary> + |Liz> + |Tom> + |Frank> + |Sarah Roberts> + |James Roberts>

And once more for luck, here is everything we now know about Fred:
sa: dump exact
----------------------------------------
|context> => |context: sw console>

supported-ops |Fred> => |op: name> + |op: height> + |op: dob> + |op: friends> + |op: mother> + |op: father>
name |Fred> => |Fred Roberts>
height |Fred> => |cm: 176>
dob |Fred> => |date: 17/5/1986>
friends |Fred> => |Mary> + |Liz> + |Tom> + |Frank>
mother |Fred> => |Sarah Roberts>
father |Fred> => |James Roberts>
----------------------------------------
And once more! We are working on knowledge representation, right? Well, one component of that is there are multiple possible representations of the same knowledge. Here is a more readable version (compared to standard BKO), the display function:
sa: display |Fred>
  Fred
  supported-ops: op: name, op: height, op: dob, op: friends, op: mother, op: father
           name: Fred Roberts
         height: cm: 176
            dob: date: 17/5/1986
        friends: Mary, Liz, Tom, Frank
         mother: Sarah Roberts
         father: James Roberts

I will write up inverse in the next post. Decided they needed separate posts.

Update: apply() is really quite useful. I probably should have put it in its own post, but too late now. Anyway, one common usage is this thing:
star |*> #=> apply(supported-ops |_self>,|_self>)
which returns everything we know about a ket in one superposition. Alternatively, we could call it:
what-do-you-know-about |*> #=> apply(supported-ops |_self>,|_self>)
Update: for a quick guide to how well you understand something (in this case "x"), you can do:
how-many supported-ops |x>

Update: now with the magic of tables:
  name |Fred> => |Fred Roberts>
  height |Fred> => |cm: 176>
  dob |Fred> => |date: 17/5/1986>
  friends |Fred> => |Mary> + |Liz> + |Tom> + |Frank>
  mother |Fred> => |Sarah Roberts>
  father |Fred> => |James Roberts>
  Fred |*> #=> apply(|_self>,|Fred>)

sa: table[op,Fred] ops |Fred>
+---------+-----------------------+
| op      | Fred                  |
+---------+-----------------------+
| name    | Fred Roberts          |
| height  | 176                   |
| dob     | 17/5/1986             |
| friends | Mary, Liz, Tom, Frank |
| mother  | Sarah Roberts         |
| father  | James Roberts         |
+---------+-----------------------+