Interleaving classical and reversible
aa r X i v : . [ c s . P L ] F e b Interleaving classical and reversible
Armando B. Matos , Luca Paolini − − − , and LucaRoversi − − − Universidade do Porto, Departamento de Ciência de Computadores [email protected] Università degli Studi di Torino, Dipartimento di Informatica, Italy {luca.paolini,luca.roversi}@unito.it
Abstract.
Given a simple recursive function, we show how to extracttwo interacting processes from it. The two processes can be described bymeans of iterative programs, one of which is intrinsically reversible, ina language that, up to minor details, belongs to the core of widely usedimperative programming languages. We implement the two processes asinterleaving synchronous
JAVA threads whose interaction is equivalent tothe recursive function they are extracted from.
Typically, scientific works on reversible computations promote reversibility as aninteresting topic, for many situations exist in which a computational activity hasto be able to retrace its steps; [10, Part I] is a standard reference to a first list ofthose situations. As a reinforcement, we think that knowing how to program ina reversible way, over time, could become a natural mental scheme, analogous torecursive and iterative programming schemes, when the computational problemto be solved contemplates its necessity. This work contributes to such an idea.We deal with reversible computing in the lines of those techniques that [10,Chapter 9] identifies as “Adding Reversibility to Irreversible Programs”.Mainly, in this work, we add reversibility to irreversible programs accordingto a basic scheme that we can intuitively describe in some steps. Given a classicalrecursive definition, we extract from it iterative reversible and classical parts thatwe can make to collaborate according to a synchronous Producer/Consumerpattern in order to implement the initially given recursion. Using Perumalla’sterminology [10, Chapter 9], it turns out that we can identify reversible aspectsin a classical computation by means of a method that sits in between “Source-to-Source Translator” and “Library-Based” approaches.In order to drive the intuition, let recF[p,b,h] (see
Listing 1.1 ) be a re-cursive function defined in a reasonable programming formalism on top of a predecessor function p , a step function h , and a base function b . We decompose recF[p,b,h] into two processes itFCls[b,h] and itFRev[p,pInv] such that: recF[p,b,h] ≃ itFCls[b,h] k itFRev[p,pInv] , (1) A. Matos, L. Paolini, L. Roversi where (i) “ ≃ ” stands for “ equivalent to ”, and “ k ” for “ interaction/parallel -composi-tion” between its two arguments; (ii) itFCls[b,h] sequentially composes b anda for -loop that iteratively applies h ; (iii) itFRev[p,pInc] sequentially composestwo for -loops one iterating b , the other its inverse bInv .On one side, (1) says that we can translate the classical language construct recF[p,b,h] as composition of two interacting parts, one of which naturallyexposes a reversible nature; so we see (1) as instance of “Source-to-Source Trans-lator” approach. On the other, concerning the “Library-Based” approach, (1)allows to look at the reversible part itFRev[p,pInv] as the component of a li-brary, with only reversible code in it, that produces the values that the classicalpart relies on to accomplish the overall task to implement recF[p,b,h] togetherwith itFCls[b,h] .In order to realize the Producer/Consumer pattern that (1) summarizes,we use a programming syntax that, up to minor syntactic details, is a nucleuscommon to C -style programming languages (nowadays ubiquitous, e.g. C++ , JAVA , C , . . . ) which can be implemented by the control structures of the tworeversible languages SRL [6,7] and
RPP [8,9,7]. We recall that
SRL and
RPP are intrinsically reversible programming notations with finite iterations only, asexpressive as the class of primitive recursive functions. This essential correspon-dence allows us to implement the Producer/Consumer pattern in terms of
JAVA synchronous threads that the interested reader can experiment with [1].The reminder of this introduction deepens, but at an intuitive level, theobservations that, starting from the idealized formalization in (1) eventuallyleads to the
JAVA prototypical implementation in [1]. Fix recF(x) { if (x==0) { b(x); } else { h(x,recF(x-1)); } } Listing 1.1: The recursive function recF . To start with, let us focus on the recursive recF of Listing 1.1 which is oneof the possible instances of recF[p,b,h] in (1), where b(x) is the base function, h(x,y) the step function, and p(x) the predecessor x-1 ; moreover, the condition c(x) , which identifies the base case, is set to x==0 . As an (almost) minor remark,some of the readers would identify recF as primitive recursive; others would sayit is not exactly as such because its inductive step has not form h(p(x),f(p(x))) .Of course, we can always to think of taking an h(x,y) that hides p(x) applied toits first argument.
Listing 1.2 reconstructs the unfolding of recF(3) , i.e. h(3,h(p(3),h(p(p(3)),h(p(p(p(3))),b(p(p(p(3)))))))) . Every of its comments asserts a property of thevalues that x or y stores. Lines 2–4 unfold an iteration that computes p(p(p(3))) , which eventually sets the value of x to . Line 5 starts the construction nterleaving classical and reversible 3 /*** Assumption: the inital value of x is 3 */ x = p(x) // ==2 x = p(x) // ==1 x = p(x) // ==0 y = b(x) // ==b(p(p(p(3)))) y = h(x,y) // ==h(p(p(p(3))),b(p(p(p(3))))) x = pInv(x) // ==pInv(p(p(p(3))))==p(p(3)) y = h(x,y) // ==h(p(p(3)),h(p(p(p(3))),b(p(p(p(3)))))) x = pInv(x) // ==pInv(p(p(3)))==p(3) y = h(x,y) // ==h(p(3),h(p(p(3)) // ,h(p(p(p(3))),b(p(p(p(3))))))) x = pInv(x) // ==pInv(p(3))==3 y = h(x,y) // ==h(3,h(p(3),h(p(p(3)) // ,h(p(p(p(3))),b(p(p(p(3)))))))) Listing 1.2: Iterative unfolding recF(3) : the bottom-up part. of the final value of recF(3) by applying the base case of recF , i.e. b(x) . Bydefinition, let pInv denote the inverse of p , i.e. pInv(p(z))==p(pInv(z))==z , forany z . Clearly, in our running example, the function pInv(x) is x+1 . Lines 6–13alternate h(x,y) , whose result y , step by step, gets better and better the finalvalue recF(3) , and pInv(x) , which produces a new value for x . /*** Assumptions. s == 0, e == 0, g == 0, w == 0 */ w = w + x; for (i = 0; i<=w; i++) { if (x> 0) { g++; } else if (x==0) { e++; } else { s++; } x = p(x); } for (i = 0; i<=w; i++) { x = pInv(x); if (x> 0) { g--; y = h(x,y); } else if (x==0) { e--; y = b(x); } else { s--; } } w = w - x; Listing 1.3: Iterative itF equivalent to recF . Let us call itF the code in
Listing recF by means of finiteiterations only. Continuing with our running example, if we run itF here abovestarting with x == 3 , then x == 0 holds at line 8, just after the first for -loop;after the second for -loop y == recF(3) holds at line 14.
A. Matos, L. Paolini, L. Roversi /*** Assumptions. s = 0, e = 0, g = 0, w = 0 */ w = w + x; for (i=0; i<=w; i++) { if (x> 0) { g++; } //number of times x is ‘g’reater than 0 else if (x==0) { e++; } //number of times x is ‘e’qual to 0 else { s++; } //number of times x is ‘s’maller than 0 x = p(x); } for (i=0; i<=w; i++) { x = pInv(x); if (x> 0) { g--; /* Value of x for h availabe here */ } else if (x==0) { e--; /* Value of x for b availabe here */ } else { s--; } } w = w - x; Listing 1.4: Reversible side of itF . Let us call reversible side of itF the code in Listing
Listing h(x,y) and b(x) at lines 11, 12. It is reversible because, when it stops,the value that every variable contains, but i , is identical to the one it containedat line 2, independently from the assumptions at line 1. The variable i does notcontradict that Listing reversible side itF has two parts. Through lines 2–7,the variable g counts how many times x remains positive, the variable e howmany it stays equal to , and the variable s how many it becomes negative. Inour running example, it will never be the case that x assumes a negative valuebecause the whole iteration at lines 3–7 is driven by the initial value of x which,initially, is assumed to be non negative, and p(x) decreases its argument of asingle unity. We will discuss about the relevance of variable s later. Lines 9–13simply undo what lines 2–7 do; the reason is that the execution of p(x) , g++ , e++ , s++ is annihilated by executing their inverses pInv(x) , g-- , e-- , s-- , respectively,in reversed order. This is why the correct values of x are available at lines 12, 11and we can use them as arguments of b(x) and h(x,y) in order to update y as in Listing recF(x) in Listing b(x) and h(x,y) at lines 12, 11 of
Listing
Listing
Listing reversible side of itF ; calling b(x) and h(x,y) in it would generate the result y , so preventingthe possibility to reset the value of every involved variable to their initial value.This is why we also need a classical side of itF that generates y in collaborationwith the reversible side in order to implement recF(x) correctly.We claim that Listing classical side itFCls and the reversible side itFCRev of itF which, suitably interacting, make the scheme (1)almost fully concrete. We see itFRev as the producer of values that the consumer itFCls consumes as soon as it uses them as arguments of the base b(x) and nterleaving classical and reversible 5 the step function h(x,y) . The following points illustrate how itFCls and itFRev synchronously interact.1. The starting point of the synchronous interaction between itFCls and itFRev is line 2 of itFCls . The comment: /* Inject the current x at line 2 of itFRev to let it start */ describes what, in a fully implemented version of itFCls , we expect in thatline of code. The comment says that itFCls injects (sends, puts) its inputvalue x to line 2 of the reversible side itFRev (cf. Listing itFRev obtains that value at its line 2, as outlined by the comment: /* Inject here the value of x from line 2 of itFCls */ its for -loop at lines 4–8 is executed.2. After executing its line 2, itFCls stops at line 3. It waits for itFRev to producethe number of times that itFCls has to iterate line 7. The comment: /* Probe line 9 of itFRev to get the number of iterations to execute */ says that we see the consumer as probing the producer itFRev until it deliversthat value; itFRev let that value be available in its variable g at line 9, asoutlined by comment: /* itFCls probes here g which has the number of iterations */ .
3. Once gotten the value in iterations , itFCls proceeds to line 5 and stopsagain. It waits for itFRev to produce the argument of b which is eventuallyavailable for probing at line 14 of itFRev .4. Once the argument becomes eventually available, then b is applied, and itFCls enters its for -loop, stopping at line 7 at every iteration. The reasonis that itFCls waits for line 12 in itFRev to produce the value of the firstargument of h(x,y) . This interleaved dialog between line 7 of itFCls and line12 of itFRev lasts iterations times.Identifying the reversible side itFRev and the classical side itFCls of theclassical recursive recF in accordance with (1) recalls Girard’s decomposition A → B ≃ ! A ⊸ B [3] which looks at a classical computation A → B as aninteraction between a linear (ideally reversible) part with type C ⊸ B , for some C , and a non-linear ! A that replicates computational resources; conclusions willdiscuss this briefly again. Contributions and road map. – Let a predecessor function p(x) be given. By definition, let ∆ p be definedas p(x)-x which, forcefully, is negative and which we assume constant. Letalso b(x) and h(x,y) be base and step functions, respectively. Section reversible side that we can extract from anyrecursive classical function recG[p,b,h] , built on b(x) , h(x,y) , p(x) , andwith a condition c(x) with form x <= 0 . This means that recG weakens thestructure of recF in Listing
A. Matos, L. Paolini, L. Roversi /*** Assumption. The value of the input x is available here */ /* Inject the current x at line 2 of itFRev to let it start */ iterations = /* Probe line 9 of itFRev to get the number of iterations to execute */ y = b(/* Probe line 14 of itFRev to get the argument */); for (i = 0; i
Section
2; the class that implements the producer synchronously interactswith the consumer, whose structure itFCls with minor structural updates.Sources of the
JAVA classes and packages are available at [1]. – Section
We show how, and justify why a recursive function recG slightly more generalthan recF in Listing reversibleside we already commented in
Listing recG that we consider is in
Listing b(x) and h(x,y) are a base and a step function, respectively. Let us recall from the introductionthat, given a predecessor p(x) , we let ∆ p be the negative difference defined as ∆ p = p(x)-x . The value of ∆ p that we consider is an arbitrary and constant k <= -1 , not only k == -1 , which requires to consider the slightly more general condition x <= 0 . For example, let p(x) be x-2 . The computation of recG(3) is h(3,h(p(3),h(p(p(3)),b(p(p(3)))))) , which looks for the least n of iteratedapplications of p(x) such that p(...p(3)...) <= 0 holds: in our case n < 3 .We call itG in Listing itF in Listing itG iteratively implements any recursive function whose structure canbe brought back to the one of recG . Please, remark that line 1 initializes ancillae s , e , g , and w , like for the namesake variables of itF , a standard initializationin reversible computing [8,10], and line 2 adds new ancillae z , predDivX , and predNotDivX .We also assume an initial non negative value for x . The reason is twofold.Firstly, it keeps our discussion as simple as possible, with no need to use theabsolute value of x to set the upper limit of every index i in the for -loopstha occur in the code. Second, negative values of x would widen our discussionabout what a classical recursive function on negative values is and about whatits reversible equivalent iteration has to be; we see this as a very interestingsubject connected to [2].We start observing that line 3 of itG sets w to the initial value of x ; the reasonis that every for -loop, but the one at lines 10–13, has to last x+1 iterations, and x changes in the course of the computation; so, w stores the initial value of x andstays constant from line 4 through line 21. In fact it can change at lines 23–33.We will see why, but eventually w is reset to its initial value at line 34.With the previous premises, given a non negative x , in analogy to itF , the for -loop at lines 4–8 of itG iterates the application of p(x) as many times as w+1 ,i.e. the initial value of x plus . So, the value of x at line 9 is equal to w+(w+1)* ∆ p which cannot be positive. In particular, all the values that x assumes in the for A. Matos, L. Paolini, L. Roversi s = 0, e = 0, g = 0, w = 0; z = 0, predDivX = 0, predNotDivX = 1; w = w + x; /* x is assumed to be the input */ for (i = 0; i <= w; i++) { if (x > 0) { g++; } else if (x == 0) { e++; } else { s++; } x = p(x); } for (i = 0; i < e; i++) { predDivX = predDivX + predNotDivX; predNotDivX = predDivX - predNotDivX; } for (j = 0; j < predDivX; j++) { for (i = 0; i <= w; i++) { x = pInv(x); if (x > 0) { g--; y = h(x,y); } else if (x == 0) { e--; y = b(x); } else { s--; }}} for (j = 0; j < predNotDivX; j++) { w++; for (i = 0; i <= w; i++) { x = pInv(x); if (x > 0) { g--; x = p(x); if (z < 0) { } else if (z == 0) { y = b(x); z++; } else { y = h(x,y); } x = pInv(x); } else if (x == 0) { e--; } else { s--; }} w--; } w = w - x; /* y carries the output */ Listing 1.8: The iterative reversible function itG equivalent to recG . nterleaving classical and reversible 9 -loop at lines 4–8 belong to the following interval: I ( w ) , [ w+(w+1)* ∆ p , w+w* ∆ p , . . . , w+ ∆ p , w ] (2)from the least to the greatest; the counters g , e , s say how many elements of I ( x ) are g reater, e qual or s maller than , respectively. Depending on to belongto I ( x ) determines the behavior of the reminder part of itG , i.e. lines 10–34.We need to distinguish two cases in order to illustrate them. As a first case , let w% ∆ p == 0 , i.e. ∆ p divides the initial value of x which w stores in it. So, ∈ I ( x ) , which implies the following relations hold at line 9: e == 1 g == - w ∆ p s == (w+1)-g-e . (3)Lines 10–12 execute exactly once, swapping predDivX and predNotDivX . We ob-serve that we could have well used the if -selection in Listing
RPP , in place of the for -loop at lines 10–12; we opt fora more compact code with no empty branches. if (e < 0) { } else if (e == 0) { predDivX = predDivX+predNotDivX; predNotDivX = predDivX - predNotDivX; } else { } Listing 1.9: A possible replacement of lines 10–12 in Listing 1.8.
Swapping predDivX and predNotDivX let predDivX == 1 and predNotDivX == 0 ,which corresponds to computationally exploit that ∆ p divides w : the for -loopbody at lines 15–19 becomes accessible, while lines 22–33, with for -loops amongthem, do not. Lines 15–19 are identical to lines 10–16 of itF in Listing b(x) and h(x,y) in order to simulatethe recursive function we start from.
As a second case , let w% ∆ p != 0 , i.e. ∆ p does not divide the initial value of x which w stores in it. So, I ( x ) , implying that: e == 0 g == - (cid:22) w ∆ p (cid:23) s == (w+1)-g-e (4)hold at line 9. Lines 11–12 cannot execute, leaving predDivX and predNotDivX asthey are: lines 22–33 become accessible and the for -loop at lines 13–19 does not.Line 22 increments w to balance the information loss that the rounding of g in(4) introduces; line 33 recovers the value of w when the outer for -loop starts.The if -selection at lines 25–32 allows to identify when to apply b(x) , which mustbe followed by the required applications of h(x,y) . We know that I ( x ) , so x == 0 can never hold. Clearly, s-- is executed until x > 0 . But the first time x> 0 holds true we must compute b(p(x)) , because the base function b(x) must beused the last time x assumes a negative value, not the first time it gets positive; lines 26–30 implement our needs. Whenever x > 0 is true, the value of x is onestep ahead the one we need: we get one step back with line 26 and, if it is thefirst time we step back, i.e. z == 0 holds, then we must execute line 28. If not,i.e. z != 0 , we must apply the step function at line 29. Line 30, restores the rightvalue of x .Finally, we observe that itG does not reset z to its initial value . The lines: for (i = 0; i < predNotDivX; i++) { z--; } Listing 1.10: Code to set z back to . which we could place before current line 34, are missing. The reason not tointroduce them is that we want to proceed according to the initial scheme (1). Weidentify a purely reversible part itGRev and a classical part itGCls in itG whichinteract, with no need of z anymore. The reason is that we delegate the controlover which between b(x) and h(x,y) to apply to the classical part itGCls . Thisresults from framing itGRev and itGCls as two JAVA threads that synchronouslycollaborate, under a Producer/Consumer template that
Section
This section comments parts of the
JAVA code in [1], that compile and run with
JAVA SE 14 - ORACLE . The section is meant to be self-contained; an intuitiveidea about what a (
JAVA ) thread should be more than sufficient to catch theessential points. Endless literature on how using
JAVA threads in details existsand [1] is based on one of those possible “infinite” instances.Our goal is to describe how
JAVA classes in [1] implement independentthreads which, synchronously collaborating in accordance with a Producer/Con-sumer template by means of a couple of communication channels, fully imple-ment both itGCls in Listing itGRev in Listing recG in Listing
ItGCls ( Listing 1.11 ), and
ItGRev ( Listing
ItGCls.itGCls and
ItGRev.itGRev , respectively. In particular,
ItGRev.itGRev fully implementsthe purely reversible part of itG in Listing
Listing recF in Listing recG .As a global assumption, B and H are two JAVA classes with ( static ) methods b(x) and h(x,y) , respectively, one implementing a base function, the other a step function. For example, downloading them from [1], one finds a
B.b(x) which isthe identity, and a
H.h(x,y) that returns x+y .As already outlined,
ItGCls in Listing itG , coherently with
Listing
ItGCls are a classical iterationwhich, given a number of iterations , and the right sequence of values in the nterleaving classical and reversible 11 public class ItGCls { private final Inject inject; private final Probe probe; private int out = 0; private int in = 0; public ItGCls(Inject inject, Probe probe, int x) { this.inject = inject; this.probe = probe; this.in = x; } public int getOut() { // Let out be available outside ItGCls return this.out; } public void itFCls() throws InterruptedException { inject.put(in); int iterations = probe.get(); out = B.b(probe.get()); for (int i = 0; i < iterations; i++) { out = H.h(probe.get(), out); } } } Listing 1.11: The consumer class
ItGCls . argument variable in , executes the sequence of assignments: out=B.b(probe.get())out=H.h(probe.get(),out); (5) out=H.h(probe.get(),out);... until out contains the result, i.e. what we call y in Listing
ItGCls sets the instances inject and probe of the two communication channels
Probe and
Inject . Lines 15, 17 obtain the required argument values of the base and of the step functions by calling probe.get() . Also line 14 calls probe.get() .It is to obtain the number of iterations . Instead, line 13 sends the initial value of in to the reversible side, i.e. (an instance of) the producer ItGRev ; ItGRev needssuch a value to start producing the sequence of arguments values that
ItGCls requires to execute the sequence (5).As told,
Listing
Listing
Listing itG ItGRev itG of Listing
ItGRev of Listing public class ItGRev { private final Inject inject; private final Probe probe; ItGRev(Inject inject, Probe probe) { this.inject = inject; this.probe = probe; } public void itGRev() throws InterruptedException { int s = 0, e = 0, g = 0, w = 0, x = 0; int predDivX = 0, predNotDivX = 1; x = inject.swapIn(x); // read x from itGCls w = w + x; for (int i = 0; i <= w; i++) { if (x > 0) { g++; } else if (x == 0) { e++; } else { s++; } x = Pred.pred(x); } for (int i = 0; i < e; i++) { predDivX = predDivX + predNotDivX; predNotDivX = predDivX - predNotDivX; } for (int j = 0; j < predDivX; j++) { probe.put(g); // send g to itGCls for iterations for (int i = 0; i <= w; i++) { x = Pred.predInv(x); if (x > 0) { g--; // send x to itGCls for h(x,y) probe.put(x); } else if (x == 0) { e--; // send x to itGCls for b(x) probe.put(x); } else { s--; }}} for (int j = 0; j < predNotDivX; j++) { probe.put(g); // send g to itGCls for iterations w++; for (int i = 0; i <= w; i++) { x = Pred.predInv(x); if (x > 0) { g--; x = Pred.pred(x); // send x to itGCls for b(x) or h(x,y) probe.put(x); x = Pred.predInv(x);} else if (x == 0) { e--; } else { s--; }} w--; } w = w - x; x = inject.swapOut(x); // restore initial x }} Listing 1.12: The (reversible) producer
ItGRev . nterleaving classical and reversible 13 We now trace the behavior of
ItGRev in analogy to the one of itG . Let w% ∆ p== 0 , so lines 21–30 are accessible. Line 21 sets the value of iterations in the consumer ItGCls , so it can move to its line 15 waiting for the argumentvalue of
B.b(x) . Lines 26, and 29 call probe.put() ; in both cases the call sets thevalue that the consumer
ItGCls waits in order to feed
H.h(x,out) or B.b(x) .Let, instead, w% ∆ p!= 0 , so lines 32–43 are accessible. Line 32 sets the valueof iterations in the consumer ItGCls exactly like line 21. Lines 36–42 simplifylines 25–32 of itG in Listing they do not need neither an if -selection nor z to identify which between B.b(x) and
H.h(x,out) to use, operation delegatedto
ItGCls which receives x from probe.put(x) at line 39.Finally, the producer ItGRev is triggered to start at line 10: inject.swapIn() swaps the content of x , ancilla local to ItGRev , with the input value, (possibly)available in
Inject.inject , shared between
ItFRev and
ItGCls . Line 45 restoresthe initial value of x by swapping it with the one it gets at line 10. public class Probe { private int x = 0; private boolean xAvailable = false; public synchronized int get() throws InterruptedException { while (!xAvailable) { wait(); } // producer has not produced int out = x; xAvailable = !xAvailable; notify(); return out; } public synchronized void put(int in) throws InterruptedException { while (xAvailable) { wait(); } // consumer has not consumed x = in; xAvailable = !xAvailable; notify(); }} Listing 1.13: Channel
Probe . Instances of classes
Probe and
Inject model channels for synchronous dia-logues between (instances of)
ItGCls and
ItGRev . JAVA ‘ synchronized ’ directivelet the bodies of every method in Probe and
Inject be critical regions, each oneaccessed by a single thread at a time, one running
ItGCls , the other
ItGRev . Letus comment on method get in Probe , all others, those ones of
Inject included,behaving analogously . Let probe.get() be a call in ItGCls of an instance probe of Probe . The consumer enters probe.get() and waits() until xAvailable is setto true , meaning that the producer
ItGRev made a value available in probe.x by We address to the official documentation on
JAVA classes
Runnable and
Threads onpossible implementations of critical regions.4 A. Matos, L. Paolini, L. Roversi means of a call to probe.put(x) . As soon as
ItGRev negates xAvailable , settingit to true , and calls notify() at line 16 in probe.put(x) , ItGCls leaves line 6 of probe.get() in order to: (i) set out with the value in x ; (ii) negate xAvailable again; (iii) notify that ItGRev can produce a new value for probe.x ; (iv) return thevalue of probe.out . Method prob.put(x) sets a symmetric behavior on
ItGRev . public class Inject { private int xInitial = 0; private boolean notSet = true; public synchronized int get() throws InterruptedException { while (notSet) { wait(); } int out = xInitial; notify(); return out; } public synchronized void put(int in) throws InterruptedException { while (!notSet) { wait(); } xInitial = in; notSet = !notSet; notify(); } public synchronized int swapIn(int in) throws InterruptedException { while (notSet) { wait(); } int out = xInitial; xInitial = in; notify(); return out; } public synchronized int swapOut(int in) throws InterruptedException { int out = xInitial; xInitial = in; notSet = !notSet; notify(); return out; }} Listing 1.14: Channel
Inject . In this work we show that given a (unary) base function b(x) , a (binary) step function h(x,y) , a (unary) predecessor p(x) that decreases every of its input x by a constant value ∆ p , so the inverse pInv(x) forcefully exists, we can decom-pose every recursive (classical) function recG[p,b,h] , built on p(x) , b(x) , and nterleaving classical and reversible 15 h(x,y) as in Listing itGRev[p,pInv] ,and the classical itGCls[b,h] , following the notation in (1). Both itGRev[p,pInv] and itGCls[b,h] synchronously cooperate to implement recG[p,b,h] as Produc-er/Consumer. Despite the simplicity of recG[p,b,h] , its decomposition requiressome work in order to soundly model the recursive unfolding process by meansof finite iterations available in intrinsically reversible programming formalismlike
SRL , and
RPP which itGRev[p,pInv] can be traced to. The decompositioninto two parts finds implementation as
JAVA threads which fit both Perumalla’s“Source-to-Source Translator” and “Library-based” approaches to reversibility.The transformation we propose shows the role that reversibility can have ineveryday programming. Let recG be matching
Listing recG recursive, typically it is simpler to provecorrect, as compared to an equivalent iterative one. A compiler should generate
ItGCls and
ItGRev equivalent to recG , such that (istances of)
ItGRev could runon a true reversible hardware, possibly contributing to leverage the promisedgreener foot-print of reversible computing, as compared to the classical one.A first natural step beyond this work is to identify a class R of recursiveschemes more general than the one in Listing R must containrecursive functions with arbitrary arity. Second, the recursive functions of R should be able to be based on predecessors p such that, at least:1. ∆ p is not necessarily a constant, as in Section
2. For example, ∆ p == -3 oneven arguments, and -2 on odd ones can be useful;2. p(x) is an integer division x/k , for some given k > 0 , like in a dichotomicsearch, that has k == 2 .More generally, we aim at an R with recursive functions only, of which every recG is defined on at least one predecessors p such that, for every argument x , a finiteiterated application p(..p(x)..)) exists which let the condition c(p(..p(x)..) of recG be true. So, for every recG[p,b,h] , p , b and h in R the introductory scheme(1) would become a compilation scheme J · K from R to, just as an example, JAVA threads “defined” as: J p K = required (reversible) code J pInv K = ! J p KJ recG[p,b,h] K = ItGCls[ J b K , J h K ] k ItGRev[ J p K , J pInv K ] , where ! J . K inverts the code that its argument J . K produces, and k models somekind communication.Also, we see the above compilation scheme a good starting point to abstractaway from the concreteness oriented perspective taken so far in this work. Thenew goal would be to investigate if the decomposition we have seen has someanalogies with Girard’s decomposition A → B ≃ ! A ⊸ B . To us, decomposing recG[p,b,h] in terms of itGCls[b,h] and itGRev[p,pInv] suggests that the rela-tion between reversible and classical computations can be formalized by a linearisomorphism A n ˛ B n between tensor products A n , and B n of A , and B , inanalogy to [5]; then we can get classical computations by applying a functor, say γ , whose purpose is, at least, to forget, or to inject replicas of, parts of A n , and B n , in a way that ( γA n → γA n ) ⊎ ( γA n ← γA n ) can be a proposal for theirtype; the type says that we pass from a reversible computation to a classical oneby choosing which is input and which is output, and by introducing freedom inthe use of the computational resources.In the same foundational vein, we conclude by observing that the decom-position of a given revG that this work introduces, can possibly lead to iden-tify a hierarchy inside the class R , once R is precisely identified. The hierarchywould characterize a function revG depending on the computational space thatthe reversible component itGRev we would extract from revG requires to work;intuitively, the space would depend on how complex the inverse pInv of the predecessor p is, and that revG is defined on. We see this as related to [12,13]. References
1. Eclipse java project rev2iterrev. https://github.com/LucaRoversi/Rec2IterRev .2. Eerke A. Boiten. Improving recursive functions by inverting the order of evaluation.
Science of Computer Programming , 18(2):139 – 179, 1992.3. Jean-Yves Girard. Linear logic.
Theoretical Computer Science , 50(1):1 – 101, 1987.4. David Gries.
The Science of Programming . Texts and Monographs in ComputerScience. Springer, New York, NY.5. Roshan P. James and Amr Sabry. Information effects. In John Field and MichaelHicks, editors,
Proceedings of the 39th ACM SIGPLAN-SIGACT Symposium onPrinciples of Programming Languages, POPL 2012, Philadelphia, Pennsylvania,USA, January 22-28, 2012 , pages 73–84. ACM, 2012.6. Armando B. Matos. Linear programs in a simple reversible language.
Theor.Comput. Sci. , 290(3):2063–2074, 2003.7. Armando B. Matos, Luca Paolini, and Luca Roversi. On the expressivity of totalreversible programming languages. In Ivan Lanese and Mariusz Rawski, editors,
Reversible Computation , pages 128–143, Cham, 2020. Springer International Pub-lishing.8. Luca Paolini, Mauro Piccolo, and Luca Roversi. On a class of reversible primitiverecursive functions and its turing-complete extensions.
New Generation Comput-ing , 36(3):233–256, Jul 2018.9. Luca Paolini, Mauro Piccolo, and Luca Roversi. A class of recursive permutationswhich is primitive recursive complete.
Theor. Comput. Sci. , 813:218–233, 2020.10. Kalyan S. Perumalla.
Introduction to Reversible Computing . Chapman & Hal-l/CRC Computational Science. Taylor & Francis, 2013.11. J. E. Rice. An Introduction to Reversible Latches.
The Computer Journal ,51(6):700–709, 01 2008.12. Paul M. B. Vitányi. Time, space, and energy in reversible computing.
CoRR ,abs/cs/0504088, 2005.13. Tetsuo Yokoyama, Holger Bock Axelsen, and Robert Glück. Optimizing reversiblesimulation of injective functions.