Thursday 24 September 2015

new functions: union[op] and top[k]

Just a couple of small new function operators:

union[op] (|w> + |x> + |y> + |z>)
returns
union(op|w>,op|x>,op|y>,op|z>)
(well, it would, but currently we only have a 2 and 3 parameter union)
A quick example:
sa: load fred-sam-friends.sw
sa: dump
----------------------------------------
|context> => |context: friends>

friends |Fred> => |Jack> + |Harry> + |Ed> + |Mary> + |Rob> + |Patrick> + |Emma> + |Charlie>

friends |Sam> => |Charlie> + |George> + |Emma> + |Jack> + |Rober> + |Frank> + |Julie>
----------------------------------------

sa: union[friends] split |Fred Sam>
|Jack> + |Harry> + |Ed> + |Mary> + |Rob> + |Patrick> + |Emma> + |Charlie> + |George> + |Rober> + |Frank> + |Julie>
Next, is: top[k]
returns the top k kets in a superposition, without changing the order. Though if several kets have the same coeff, then it will return more than k results.
A quick example:
sa: top[0] (|a> + 3|b> + 2|c> + 9.3|d> + 0.5|e>)
|>

sa: top[1] (|a> + 3|b> + 2|c> + 9.3|d> + 0.5|e>)
9.3|d>

sa: top[3] (|a> + 3|b> + 2|c> + 9.3|d> + 0.5|e>)
3|b> + 2|c> + 9.3|d>

sa: top[10] (|a> + 3|b> + 2|c> + 9.3|d> + 0.5|e>)
|a> + 3|b> + 2|c> + 9.3|d> + 0.5|e>
And an example where more than 1 ket has the same coeff:
sa: top[1] (3.2|a> + |b> + 3.2|c> + 3.2|d> + 3|e> + |f> + |g> + 3.1|h> + 3.2|i>)
3.2|a> + 3.2|c> + 3.2|d> + 3.2|i>

sa: top[2] (3.2|a> + |b> + 3.2|c> + 3.2|d> + 3|e> + |f> + |g> + 3.1|h> + 3.2|i>)
3.2|a> + 3.2|c> + 3.2|d> + 3.2|i>

sa: top[3] (3.2|a> + |b> + 3.2|c> + 3.2|d> + 3|e> + |f> + |g> + 3.1|h> + 3.2|i>)
3.2|a> + 3.2|c> + 3.2|d> + 3.2|i>

sa: top[4] (3.2|a> + |b> + 3.2|c> + 3.2|d> + 3|e> + |f> + |g> + 3.1|h> + 3.2|i>)
3.2|a> + 3.2|c> + 3.2|d> + 3.2|i>

sa: top[5] (3.2|a> + |b> + 3.2|c> + 3.2|d> + 3|e> + |f> + |g> + 3.1|h> + 3.2|i>)
3.2|a> + 3.2|c> + 3.2|d> + 3.1|h> + 3.2|i>

sa: top[6] (3.2|a> + |b> + 3.2|c> + 3.2|d> + 3|e> + |f> + |g> + 3.1|h> + 3.2|i>)
3.2|a> + 3.2|c> + 3.2|d> + 3|e> + 3.1|h> + 3.2|i>

sa: top[7] (3.2|a> + |b> + 3.2|c> + 3.2|d> + 3|e> + |f> + |g> + 3.1|h> + 3.2|i>)
3.2|a> + |b> + 3.2|c> + 3.2|d> + 3|e> + |f> + |g> + 3.1|h> + 3.2|i>
Note, it doesn't give the expected result when more than 1 ket has the same coeff. I guess we could tweak it to give exactly k results, but then we would have to pick randomly from the set of kets with the same coeff. We could do that in code easy enough, but I'm not sure we want that as the default behaviour.

Saturday 12 September 2015

representing nuclear decay

The idea is simple enough. Let's use our new function process-reaction() to encode an example of nuclear decay. In this case fission of U_235, but the idea is general enough to extend to other nuclear and chemical reactions.

First, let's learn some example knowledge about fission of U_235:
sa: load uranium-fission.sw
sa: dump
----------------------------------------
|context> => |context: uranium fission products>

fission-channel-1 |U: 235> => |Ba: 141> + |Kr: 92> + 3|n>
fission-channel-2 |U: 235> => |Xe: 140> + |Sr: 94> + 2|n>
fission-channel-3 |U: 235> => |La: 143> + |Br: 90> + 3|n>
fission-channel-4 |U: 235> => |Cs: 137> + |Rb: 96> + 3|n>
fission-channel-5 |U: 235> => |I: 131> + |Y: 89> + 16|n>
list-of-fission-channels |U: 235> => |op: fission-channel-1> + |op: fission-channel-2> + |op: fission-channel-3> + |op: fission-channel-4> + |op: fission-channel-5>

fission |*> #=> apply(weighted-pick-elt list-of-fission-channels |_self>,|_self>)
----------------------------------------
And a note that if we had probabilities for the different fission channels, we could encode that with coeffs other than 1. And this leads to an interesting thought. Standard QM needs complex numbers to work, so we can't do that with the current BKO. But what we can do is represent the results of QM calculations, ie the resulting probabilities, in BKO. The above fission example is the first hint of this idea.

Now, let's fission our uranium:
sa: fission |U: 235>
|I: 131> + |Y: 89> + 16|n>

-- it's random, so if we try again we should get a different result:
sa: fission |U: 235>
|Xe: 140> + |Sr: 94> + 2|n>

sa: fission |U: 235>
|La: 143> + |Br: 90> + 3|n>

sa: fission |U: 235>
|Cs: 137> + |Rb: 96> + 3|n>

sa: fission |U: 235>
|Ba: 141> + |Kr: 92> + 3|n>

sa: fission |U: 235>
|I: 131> + |Y: 89> + 16|n>
This is already fun! But we can now use this to encode the full nuclear reaction:
n + U_235 -> fission-of(U_235)
Let's start with 3 neutrons and 4 atoms of U_235: 3|n> + 4|U: 235>
sa: process-reaction(3|n> + 4|U: 235>,|n> + |U: 235>,fission |U: 235>)
4|n> + 3|U: 235> + |Xe: 140> + |Sr: 94>

-- now again, this time starting with: 4|n> + 3|U: 235> + |Xe: 140> + |Sr: 94>
sa: process-reaction(4|n> + 3|U: 235> + |Xe: 140> + |Sr: 94>,|n> + |U: 235>,fission |U: 235>)
6|n> + 2|U: 235> + |Xe: 140> + |Sr: 94> + |La: 143> + |Br: 90>

-- and again. Feed in result from last reaction:
sa: process-reaction(6|n> + 2|U: 235> + |Xe: 140> + |Sr: 94> + |La: 143> + |Br: 90>,|n> + |U: 235>,fission |U: 235>)
8|n> + |U: 235> + |Xe: 140> + |Sr: 94> + 2|La: 143> + 2|Br: 90>

-- and again!
sa: process-reaction(8|n> + |U: 235> + |Xe: 140> + |Sr: 94> + 2|La: 143> + 2|Br: 90>,|n> + |U: 235>,fission |U: 235>)
23|n> + |Xe: 140> + |Sr: 94> + 2|La: 143> + 2|Br: 90> + |I: 131> + |Y: 89>

-- and again. Note, we have no U_235 left.
sa: process-reaction(23|n> + |Xe: 140> + |Sr: 94> + 2|La: 143> + 2|Br: 90> + |I: 131> + |Y: 89>,|n> + |U: 235>,fission |U: 235>)
23|n> + |Xe: 140> + |Sr: 94> + 2|La: 143> + 2|Br: 90> + |I: 131> + |Y: 89>
Noting that with no U_235 left, this final process-reaction() didn't change anything.

Then when we get around to implementing learn-sp rules, as mentioned in my last post, we can tidy this to:
fission-uranium-235 (*) #=> process-reaction(|_self>,|n> + |U: 235>,fission |U: 235>)
And then we should be able to do say 5 reactions in a row, starting with say 3 neutrons and 4 atoms of U_235:
fission-uranium-235^5 (3|n> + 4|U: 235>)
This structure is presumably general enough to represent most chemical and nuclear reactions. And we are not limited to just one reaction type. For example:
chemical-reaction-1 (*) #=> process-reaction(|_self>,|x> + |y>,3|z>)
chemical-reaction-2 (*) #=> process-reaction(|_self>,|z>,2|a> + |b>)

|resulting bag of chemicals> => chemical-reaction-1 chemical-reaction-2^3 chemical-reaction-1 starting-bag-of-chemicals
And I suspect process-reaction() could be useful elsewhere, including things other than chemical/nuclear reactions. Eg I suspect we can use it to encode quantum entanglement too!

Update: The comment on process-reaction() being useful elsewhere. I have a couple of things in mind. One is to rename parts of a superposition. I don't yet have a concrete use-case in mind, but think it might be useful. The other is a version of pattern recognition. ie, something that is slightly different to our current similar[op]. Need to give that idea a little more thought though.

new function: process-reaction

I was thinking, how can we encode reactions (eg, chemical or nuclear) into the BKO knowledge representation scheme? I eventually decided we can't really do it at the moment, but with a couple of additions, then yes we can.

The first part of this is a new function, process-reaction().
Here is the python:
# one, two and three are superpositions
def process_reaction(one,two,three):
  if intersection(two,one).count_sum() != two.count_sum():
    return one
  else:
    return intersection_fn(del_fn3,one,two).drop() + three  
Here is a simple example, making water from hydrogen and oxygen gas:
2H2 + O2 -> 2 H2O

sa: process-reaction(5|H2> + 7|O2>,2|H2> + |O2>,2|H2O>)
3|H2> + 6|O2> + 2|H2O>
I'll give a more interesting example in the next post.

The next bit is we need a way to learn superposition rules. We also have a separate motivation for this idea. That is, we need a way so that literal operators can act on superpositions, instead of just being linear. An example I like to give is:
sa: M |yes> => |yes> + -1|no>
sa: M |no> => -1|yes> + |no>
sa: matrix[M]
[ no  ] = [ 1 -1 ] [ no  ]
[ yes ]   [ -1 1 ] [ yes ]

sa: drop M (|yes> + |no>)
|>

sa: drop M (0.8|yes> + 0.2|no>)
0.6|yes>
OK. So that works as expected/desired (a simple example of inhibition). But if we try using literal operators:
sa: clean-M |*> #=> drop M |_self>

sa: clean-M (|yes> + |no>)
|yes> + |no>

sa: clean-M (0.8|yes> + 0.2|no>)
0.8|yes> + 0.2|no>
ie, it didn't work at all! This is because literal operators are currently linear.
eg:
clean-M (|yes> + |no>)
-- expands to:
clean-M |yes> + clean-M |no>
The solution (which I have not yet implemented in code) is:
learn-a-sp-rule (*) #=> foo( |_self>)
and then when we invoke it, replace the SP on the left into |_self>.
And so we can now learn:
make-water (*) #=> process-reaction(|_self>,2|H2> + |O2>,2|H2O>)
clean-M (*) #=> drop M |_self>
Now I need to find the cleanest way to implement this. It is going to take some work in at least context.learn() and context.recall() and the parser. So yeah, quite a bit of work!