Talk:Picofuck

Thanks for dropping by. Please keep general talk to the Chatter section.

Candidate languages
Languages that are candidates for PF will be provided here. They will be titled PFC0, PFC1, etc. in the order in which they are discovered. For clarity and consistency, please use the characters [ and ] to represent PFC commands (see Property II for an explanation).

PFC0
PFC0 is not PF. See talk below for the proof.

This one cannot work. There is no way to translate an RBF * which starts at the rightmost 1 on the tape into something that halts. --Ørjan (talk) 23:42, 28 March 2017 (UTC)
 * Too bad :( Do you have an easy way to see that? It's not immediately clear to me. Orby (talk) 23:50, 28 March 2017 (UTC)
 * Oops, scratch that, I somehow assumed the initial (> had to be followed by a )*(<). I now see that (>(>)*(<))*(<) halts just fine (unless I messed that up). Reversible languages are confusing... --Ørjan (talk) 23:59, 28 March 2017 (UTC)
 * I find myself in a perpetual state of confusion and awe. Orby (talk) 00:10, 29 March 2017 (UTC)
 * Hm, or wait. If a program starts with n copies of (> before the first )*(<), then it will not halt if it's on the first of n 1s, when those are at the end of the tape. So for every nontrivial PFC0 program, there is some initial tape it doesn't halt on. --Ørjan (talk) 00:15, 29 March 2017 (UTC)
 * Ah, yes I follow your argument. Therefore it is not possible to translate * because * halts on all initial tapes. Thus PFC0 cannot be PF. Orby (talk) 01:15, 29 March 2017 (UTC)
 * I've summarized this argument as Picofuck for future reference Orby (talk) 01:57, 29 March 2017 (UTC)

PFC1
PFC1 is not PF.

The program [] = *(*(>)*)< = *< halts for all inputs and always modifies the tape, satisfying properties I and III. The program = *(*(*(>)*)<*(>)*)< does not halt on the empty tape, satisfying property IV. Property II is also satisfied. Orby (talk) 15:21, 29 March 2017 (UTC)


 * I think I was a little hasty in posting this. Any number of nested []'s will never halt for a zeroed tape and any number of sequential []'s will amount to *<*<...*<, so it's impossible to shift right on an empty tape in PFC1. Don't think this can be PF. Thoughts? Orby (talk) 15:27, 29 March 2017 (UTC)


 * No, this is wrong. A program just can't start with a nested loop without entering an infinite loop on a zeroed tape. We can certainly have things like

[]

which results in <<*>> I think. Though I'm still not convinced that it's possible to shift right past the initial tape head position... Orby (talk) 15:31, 29 March 2017 (UTC)


 * Yeah the first time a program shifts right on an initially zeroed tape, it shifts into a zero and cannot possibly get out of the (>) loop. --Ørjan (talk) 16:39, 29 March 2017 (UTC)


 * OK, I see the problem clearly now. It's impossible to shift right on the right-most 1 without entering an infinite loop, which means it's impossible to translate >. Not PF :( Orby (talk) 16:45, 29 March 2017 (UTC)

PFC2
The thinking behind PFC2 is that the translation of [] to RBC results in a program that always halts (thus it satisfies property I) Edit: This is incorrect, see below. It satisfies property II clearly. Between [] and [][], there exists a program which modifies the tape for any initial machine state, so it satisfies property III. Moreover, [] may result in either left or right shifts depending on the state (it avoids pitfalls explored in non-existence proof #2). I think we're making slow but steady progress. The primary open question concerning PFC2 is satisfies property IV. Orby (talk) 20:27, 29 March 2017 (UTC)


 * Um, I get that [] doesn't halt if it starts on a 0 before a 1 with the rest of the tape to the left zero. --Ørjan (talk) 23:30, 29 March 2017 (UTC)


 * You're right. I really need to write an RBF interpreter and stop doing these things by hand... Orby (talk) 23:48, 29 March 2017 (UTC)


 * Even more seriously, the reverse of (<(>)*)* doesn't seem to halt on a zero tape, which means no PFC2 program halts in reverse on a zero tape. I think possibly a RBF program cannot halt on all tapes unless its reverse does the same, although I don't quite have a proof yet. --Ørjan (talk) 23:52, 29 March 2017 (UTC)
 * Do you have a counter example for conjecture 2.1? Orby (talk) 23:55, 29 March 2017 (UTC)

PFC3
The first PFC with unbalanced parenthesis. An improvement over PFC2. The PFC3 program []] translates to the PFC2 program [], and as a result properties I, II, and III hold for PFC3 (Edit: property I has not been shown to hold, as it turns out that it doesn't necessarily hold for PFC2). Additionally, property IV can be seen to hold for PFC3 by examining the program [][]]] which is equivalent to the RBF code

*(>(<)*)*(<(>)**(>(<)*)*(<(>)*)*)*

which runs indefinitely if started on the left-most 1. Orby (talk) 23:24, 29 March 2017 (UTC)

Property I cannot hold, as the infinite loop on 0 before 1 for PFC2's [] requires only the *(>(<)*) part which is entirely within PFC3's [. --Ørjan (talk) 00:12, 30 March 2017 (UTC)

Properties of PF languages
There are many properties a PF language must have. Demonstrating that a candidate language violates one of these properties is the easiest way to prove it is not PF.

Property I
There exists a program such that for all machine states that program halts.

There are 3 commands in RBF that halt for all possible states: *, >, and <. Therefore, a language cannot be PF if for all programs there exists a state for which that program will not halt. See the proof that PFC0 is not PF for an example. Credited to Ørjan.

Property II
The first PF command must translate to more ('s than )'s and the second PF command must translate to more )'s than ('s.

The first PF command, which we'll call [, must be translated into more ('s than )'s otherwise we cannot produce the RBF command (. Conversely, the other PF command, which we'll call ], must be translated into more )'s than ('s otherwise we cannot produce the RBF command ). Because an RBF program may start with *, >, <, or (, the translation of each of these commands must start with [ in PF otherwise ] would translate into a ) with no match. Credited to Ørjan.

Property III
There is no machine state for which all programs do not alter the tape.

The RBF command * will alter the tape regardless of the machine state. If there exists a machine state for which all programs in a language do not alter the tape, then that language cannot be PF. This implies that [] must translate to an expression which contains a * outside of a loop (otherwise having an initial tape set to all zeros will never be modified).

Property IV
There exists a program which does not halt for some machine state.

The RBF program *(>) doesn't halt on a zeroed tape. Thus, there must be a PF program which essentially does the same.

Chatter
Hello! Orby (talk) 22:38, 28 March 2017 (UTC)

Your name "Reversible Boolfuck" is confusing since I had already used that name in the article on Reversible Brainfuck, for the 1-bit version. :( --Ørjan (talk) 23:28, 28 March 2017 (UTC)


 * Oops, sorry about that. I'll change it to Reversible Bitfuck. I don't see that in use anywhere on the Wiki :) Orby (talk) 23:40, 28 March 2017 (UTC)

I'm considering trying to brute-force the search computationally. Should be pretty easy to search for 2 command languages that are simple translations of Nanofuck, aside from the issue of the halting problem.

Observation: In order to be able to translate (, one of the two Picofuck commands (call it A) must be translated into more ('s than )'s, and vice versa for ) (call the other command B). A then has to start the program, and you need to avoid something like my n 1's argument from working for whichever commands you choose. In other words, there must be some n such that you cannot set up an initial tape that forces every program starting with AAA...AAAB (n A's) not to halt. --Ørjan (talk) 00:30, 29 March 2017 (UTC)
 * And because an RBF program can start with *, <, >, or (, the translation of each of these commands must start with A otherwise we get an unmatched ) (summarized as Picofuck Orby (talk) 01:59, 29 March 2017 (UTC)

More observations: --Ørjan (talk) 03:31, 29 March 2017 (UTC)
 * We haven't actually proved yet that the number of surplus ('s in [ must be the same as the number of surplus )'s in ]. Maybe a properly matched program is ] or the like.
 * All RBF programs that don't contain are equivalent to either a NOP or to *. Proof:, (*) and ** are NOPs, and any other such program must contain one of them, which can be removed.

Non-existence proof?
Working on a non-existence proof with the hopes of either completing it or finding PF0 in the gaps. Here's what I've got so far:

Every program must contain a loop by property II. Thus, without loss of generalization, we can translate every PF program to an RBF program of the form a0(a1(...an(b)c where a0, a1, ..., an only contain the RBF commands *, >, < and b and c contain *, >, <, (, or ).

Let T denote the set of all machine states where the tape head is pointing at a 1 bit. There exists a set of initial machine states X such that for all x in X, a0(x) is in T. That is, if the machine starts in any state in X, a1 is guaranteed to run. (Note: I'm assuming X can never be empty...).

Now, either there exists x in X such that a1(a0(x)) is in T or not. If not, then the loop containing a2 can never execute and we can assume n = 1 without loss of generality. Apply this process inductively to arrive at the execution of b.

Now, I'd like to be able to say that there is some state x in X such that a0(a1(...an(x)) will put b in an infinite loop. That would imply that for every program, there is an initial state which puts the program in an infinite loop which means we can't express *, >, or < in PF, but that means


 * 1) b must result in net head movement
 * 2) The execution of a0, ..., an must not "filter" the bad states out which would cause b to enter an infinite loop.

Alternately, if the execution of a0, ..., an always filters those bad states out, then b can never enter an infinite loop which might be a way to prove that no program in the language can halt, which also means its not PF. Hmmmm...

Orby (talk) 05:16, 29 March 2017 (UTC)

Alas, that's too simple. E.g. if a1 is *, then a2 is filtered out - but only on the first iteration through the a1 loop, on any others it is run. E.g. these programs are the same up to the start of c, but the first one always halts, and the second one runs indefinitely on a zeroed tape:

*(*(>)*) *(*(>))

Things should get even more complicated if > or < is involved in the ais.

--Ørjan (talk) 13:41, 29 March 2017 (UTC)


 * Ah yes, you are right. Thanks for pointing that out. It looks like the first program will always halt. Now the question is, is it possible to divide the first program into two PF commands in such a way that it is possible to create a program that doesn't halt? Say [ = *( and ] = *(>)*) then = *(*(*(>)*)*(>)*) which is an infinite loop on a zeroed tape I think. Thus my approach for the non-existence proof is no good. But this is progress; with some modification [=*(, ]=*(>)*) might lead to PFC1. Orby (talk) 14:48, 29 March 2017 (UTC)

Thoughts: A possible method for generating a PF candidate is to construct an RBF program that halts on all inputs, then split the program into two substrings, one which we'll call [ and one which we'll call ] and find a way to construct a program out of [ and ] that runs infinitely for some initial state. That will guarantee that the PFC satisfies properties I and IV. Property II is trivial to satisfy and property III can be satisfied by making sure there is a * before the first ( in [ or after the last ) in ]. Orby (talk) 16:51, 29 March 2017 (UTC)
 * I'm playing with this technique to split *(*(>)*)*(*(<)*)* Clearly it needs to be split next to in order to avoid the problem with PFC1. Orby (talk) 18:01, 29 March 2017 (UTC)
 * Hmm, that won't work because we'd need to split next to both > and <. Orby (talk) 18:13, 29 March 2017 (UTC)

Non-existence proof #2
This one is pretty simple but hinges on a conjecture I haven't proved yet.

Conjecture 2.1
Any loop that results in net tape head movement for some initial state, also runs indefinitely for some initial state.

Examples:

*(>*(<))

Runs indefinitely on

01111111... ^

Lemma 2.2
For any PF language, all programs can be translated to RBF in the form a(b)c where the and a and c consist of the RBF commands *, >, < and b is some RBF program. Notice that a and c are fixed for all programs generated by the language because [ must start with a and ] must end with c and each PF program must begin with [ and end with ].


 * I don't see this lemma, what about programs with more than one toplevel loop, like a(b)c(d)e ? --Ørjan (talk) 00:38, 30 March 2017 (UTC)

Proof
Let a(b1)c denote the PF program which translates to the RBF program >. By (2.1), (b1) must never result in net head movement, because > never runs indefinitely. Thus, the net head movement of ac must be >. Let a(b2)c denote the PF program which translates to the RBF program <. By (2.1), (b2) must never result in net head movement, because < never runs indefinitely. Thus the net head movement of ac must be <. The net head movement of ac cannot be both > and <, therefore no PF languages exist.

QED

Counter example for 2.1
I thought this was a counter example, but there's a mistake in it Orby (talk) 23:54, 29 March 2017 (UTC)

The RBF program

*(>(<)*)

results in net head movement if run on a zeroed tape but never enters an infinite loop as it only touches the first two cells (we'll call them cells a and b) and results in the following operations depending on the content of the cells

ab --- 00     *>* 01      * '''This is wrong. If we start one cell left of the left-most 1, we enter an infinite loop.''' 10     NOP 11     NOP

Verify? Edit: Wrong.

This counter-example has brought me to the interesting RBF program

*(>(<)*)*(<(>)*)*

If the tape head starts on b, the following states result in the following results abc x00     *>* x01     * 01x     < 11x     *

where x indicates that the result is the same for both 0 and 1. Orby (talk) 19:50, 29 March 2017 (UTC)
 * I've used this program to define PFC2. Looks interesting, but might not be able to produce infinite loops. Orby (talk) 20:30, 29 March 2017 (UTC)

Non-existence Proof 3
Thanks for bearing with me through my half baked proofs and cascades of edits. Here is what I was trying to say in non-existence proof #2 but failed to express clearly. Hopefully this makes a little more sense.

Let PFi be some PF language with commands [ and ].

Lemma 3.1
The command [ must translate to at least one unmatched ( and the command ] must translate to at least one unmatched ) in order to translate the RBF commands ( and ). Moreover, [ cannot contain any umatched ) otherwise we cannot start or end a program with [. Similarly, ] cannot contain any unmatched ( otherwise we can't end a program with ]. Notice this implies that every PFi program must start with [ and end with ].

Lemma 3.2
The translation of [ to RBF is of the form

A(B1)C1...(Bn)Cn(D

where
 * A and Ci consist of the commands *, >, and <
 * Bi are RBF programs
 * D consists of the commands *, >, <, (, and ) with no ) matching the open (

Similarly, the translation of [ to RBF is of the form

Z)Y1(X1)...Ym(Xm)W

where
 * W and Yi consist of the commands *, >, and <
 * Xi are RBF programs
 * Z consists of the commands *, >, <, (, and ) with no ( matching the open )

This follows from 3.1.

Conjecture 3.3
We define a shift loop as an RBF loop that results in net tape head movement for some initial state. Examples of shift loops include

(>) (>(<))

etc. Conjecture: Every shift loop executes indefinitely for some initial state.

Theorem 3.4
Because any PFi program must start with [ and end with ] by 3.1 and the form provided to us by 3.2, we have that every PFi program must translate to an RBF program that looks like concatenations of units of the form

A(B1)C1...(Bn)Cn(Ui)Y1(X1)...Ym(Xm)W

where Ui are RBF programs which we will call kernels. Let Ui denote the kernels of the PFi program which translates to > and let Vi denote the kernels of the PFi program which translates to <. Since neither > nor < execute indefinitely for any initial state, it must be true that (Bi), (Xi), (Ui), and (Vi) are not shift loops by 3.3. That means that the net tape head movement must be a result of the execution of top-level (i.e. not inside of loops)

T = AC1...CnY1...YmW

The net tape head movement resulting from the execution of T is either always to the right or always to the left. A PFi program can execute T multiple times, but cannot change the direction of the tape head movement. We know T must result in right net tape head movement because we can express >. But it also must be true that T results in left net tape head movement because we can express <. Clearly, T cannot do both. Therefore PFi cannot exist.

Conclusions
Excluding errors in our reasoning, the only hope for constructing a PF language is finding a counter-example to 3.3 and using that to make arbitrary shifting possible.

-- Orby (talk) 03:17, 30 March 2017 (UTC)

Conjecture counterexample
Apparently it was a matter of finding the right building blocks. Here is a xor:

(>*<)

From that, we can get a swap:

(>*<)>(<*>)<(>*<)

And then we get the first counterexample to the conjecture:

((>*<)>(<*>)<(>*<)>)

This swaps the current cell with the cell to the right only if the current cell is 1. Thus the loop can run at most once, but may shift position.

We can also swap unconditionally and move right:

((>*<)>(<*>)<(>*<)>)*((>*<)>(<*>)<(>*<)>)*

Combining this, we get an unconditional move right where the movements outside the loops are balanced:

(>*<)>(<*>)<(>*<)((>*<)>(<*>)<(>*<)>)*((>*<)>(<*>)<(>*<)>)*

There has to be some movement outside the loops to implement a, because skipping or passing through a loop always ends at a cell with the same value as the original value of the cell it started at - which means that if there are no movements outside loops, then the change in "value of current cell" from beginning to end is statically known.

--Ørjan (talk) 05:10, 8 April 2017 (UTC)

Brilliant! I'll be digging into using this to conjure up a new candidate. B) Orby (talk) 19:34, 12 April 2017 (UTC)

I'm not convinced that (>*<)>(<*>)<(>*<)((>*<)>(<*>)<(>*<)>)*((>*<)>(<*>)<(>*<)>)* results in a single unconditional move right (if I'm understanding you correctly). I get what you're going for with the  pattern, which works if the loops don't shift, but in this case I think it does not. Consider

100 ^

We know  transforms it to

010 ^

Then  swaps it back, shifts right, and toggles the bit resulting in

110 ^

Applying  again, swaps to the right of that, shifts, and toggles

100  ^

Shifting right twice! I'm going to write an RBF interpreter on Friday to facilitate this stuff Orby (talk) 00:33, 13 April 2017 (UTC)


 * No,  doesn't swap it back. It only runs when the current cell is 1. It works because the second outer loop runs if and only if the first doesn't. --Ørjan (talk) 00:46, 13 April 2017 (UTC)
 * Right, right. I get it now and agree. --Orby (talk) 19:16, 13 April 2017 (UTC)

Observation
I think this is correct, let me know if you see any problems with my thinking. Consider the case where [] is a well formed program (balanced parenthesis).

[  A(A' ]   B')B

Then the translation of ( must end with [, which implies that A(A' can only contain a single open paren (i.e. A and A' must be well formed programs with no open parens). Similarly, the translation of ) must begin with ], which implies that B')B can only contain a single open paren (i.e. B' and B must be well formed programs with no open parens).

If the translation of ( ends with [, then A' must be a nop, otherwise we cannot translate (. If the translation of ) begins with ], then B' must be a nop, otherwise we cannot translate ). So we really must have the form

[ A( ] )B

where A and B are well formed RBF programs with no open parens.

Notice that the translation of ( must end with [ and it must be preceded by a well formed program. Thus the translation of ( into PF must translate back into RBF as CA( where C is some RBF program which is the inverse of A. Thus there exists a PF program which translates to the inverse of A in RBF.

I see two problems. First, it's not clear that the translation of ( must end with [, secondly, it is not clear that the translation of [ can only contain one (. To see that, consider the following two simple translations from RBF to itself:

( ( )  ) other commands to themselves This works because is a NOP.

( (  )) other commands to themselves This works because if one of the command copies is entered or exited, then the other must be as well. --Ørjan (talk) 00:14, 14 April 2017 (UTC)

A different way of thinking about the problem
Let's think about this from a different angle: Consider the RBF tape finite. What is right shift? The permutation (1 2 ... n). What is left shift? The permutation (n n-1 ... 1). The symmetric group Sn is generated by T = {(1 2), (2 3), (3 4), ..., (n-1 n), (n 1)}, that is, the set of all transpositions that swap right. Therefore right shift and left shift can be expressed in terms of swaps on a finite tape. Swapping the current cell with the cell to the right is simple in RBF, as we know:

(>*<)>(<*>)<(>*<)

If the tape is infinite, then the set of all permutations that only rearrange a finite number of elements is clearly also generated by T (though clearly not the set of all permutations on the tape, which is not even computable). Is this in addition to a universal logic gate sufficient for Turing completeness? Not quite, as if 'only' adjacent swaps and some universal logic gate are possible, then every program halts, which we know does not suffice. But maybe it's close?

Let's assume it is and continue down this path. Then it might be useful to focus on constructing two commands that, in conjunction, can produce a swap of two adjacent elements and can compute a reversible universal logic gate (Fredkin or Toffoli). It won't necessarily satisfy our goal of producing a simple translation of RBF in 2 commands, but it may deepen our understanding.

Example that can produce a Toffoli gate, but I don't see an obvious way to do swaps: [  >*( ]   )< Initial state (all programs return the data pointer to a): abcd ^ A few simple programs: [] = >*< = >*<                                            b = ~b = >*(>*<)< = >*(>*<)<                                b = ~b, c = ~b ^ c [] = >*(>*(>*<)<)<) = >*(>*(>*<)<)<                    b = ~b, c = c ^ ~b,                                                              d = d ^ ((c ^ ~b) & ~b)                                                                = d ^ (~b & ~c) Slightly more complex: [] = >*<>*(>*<)< = >*<>*(>*<)< = >(>*<)<             c = b ^ c ][[] = >*(>(>*<)<)<                                      b = ~b, d = d ^ (~b & c) Toffoli gate: []][[] = >*<>*(>(>*<)< = >*<>*(>(>*<)<)< = >(>(>*<)<)< d = d ^ (b & c)

This seems like a possible way to gain some traction. -- Orby (talk) 17:20, 14 April 2017 (UTC)

This example has the property that [A1][A2]...[Ai] is equivalent to If the cell to the right is 0 then >A1A3...A(if i is odd then i else i-1)< else >A2A4...A(if i is odd then i-1 else i)< If i is odd then >*<

In particular, you can change a cell based on any condition of cells to the left of it (except the first, which is sort of lost), but not based on any condition involving the cells to its right. So a swap is impossible. --Ørjan (talk) 01:12, 15 April 2017 (UTC)

Another example
It is also trivial to express swapping arbitrary adjacent cells. Consider the translation [  (>*<)>(<*>)<(>*<)> ]   <

If the tape state is abcde ^ Then the first few transpositions are {ab} := [] {bc} := [] {cd} := []][[]

Which we can define recursively as {0 1} := [] {i i+1} := [][{i-1 i}]

Is this just as good as > and < in terms of Turing completeness?


 * Certainly not, with those a program can only reach finitely many cells. --Ørjan (talk) 01:17, 15 April 2017 (UTC)
 * Never mind, since [ and ] don't have to match you have themselves easily. --Ørjan (talk) 01:32, 15 April 2017 (UTC)

Yet another example
Say we have the translation [ >>*(<(<*>)<(>*<)>(<*>)>)< ] < The commands [ and ] can be summarized as (let TH denote tapehead) [  Toggle TH+2. If TH+2, swap TH+0 and TH+1. Shift right. ]  Shift left. If we say [] maps (a, b, c) to (a', b', c'), then we have that c' = ~c and if b = 0 then b' = a & ~c (i.e. (a -/-> c)). This means that [] is functionally complete since ~ and -\-> in combination can express any logical connective.

Furthermore, [][]                   {ab} [][][][                > ]                       < So, [][][]][           {bc} [][][]][][][[][]   {cd} etc. So we can express any permutation that rearranges a finite number of elements on the tape with [] as well. The only thing that is missing is looping. I think a loop around the program would be sufficient for Turing completeness. It's not a simple translation of RBF, but it is interesting.