Shuvendu Lahir: Hi. So thanks for coming. ... visiting from Utah. And with the student travel who's...

advertisement
Shuvendu Lahir: Hi. So thanks for coming. So it's my great pleasure to have Zvonimir Rakamaric
visiting from Utah. And with the student travel who's in the audience. And Zvonimir is well known to
us. He's visited us as an intern two times and he did work that is still being done both here and
[indiscernible]. And he does SMACK too, which is pretty popular to compile [indiscernible]. So a lot
of people are using his tools. And today he's going to talk about some new directions that has been
pursued.
So welcome.
Zvonimir Rakamaric: Thanks a lot Shuvendu.
It's certainly nice to be back. So we can keep this informal. Clearly there is a lot of people, so feel free
to stop me at any point. We can discuss various things and so on.
So yeah, today I'll talk about this ongoing work, which is cooperation with also Shuvendu, which is on
using SMT-based verification for reasoning about approximations and programs.
And before I start, this is my wonderful group of students. We kind of recreated the one of the photos
that you maybe saw before. And if you want to find out more about other things that we're working on,
you can go to this web address.
And again, apart from the work that I'm talking about here, we do lots of other stuff related to software
verification and so on. We do analysis of [indiscernible]. We are working on the SMACK tool that
Shuvendu mentioned that translates LMIR into Boogie to flow. We start working on security and
safety of kernel modules and protocol stacks; we still work on analysis of concurrent programs, such as
Pthreads, OpenMP and GPU, and so on. Kind of doubled a little bit in Android malware detection.
And also in automatic testing of Java and the NASA software. So if you're interested if any of these,
I'm still here for two more days, so just let me know and we can talk about it in more details.
So the work I'm going to cover in this talk is on approximate computing and it's done with a bunch of
collaborators. Shaobo is here, Shuvendu as well. And then part of this work is done with
[indiscernible] Wei-Fan and [indiscernible] Ganesh Gopalakrishnan.
A brief roadmap about this talk. A motivator on proximate computing first with a couple of slides and
then I'll introduce our approach and also why we think it's a good approach. And then I'll talk about in
much more detail about how we verify safety and accuracy of approximate program, as well as
[indiscernible] determination. And we'll see how much time I have in the end.
I do have this separate work on synthesis of floating-point approximations. And like I said, we'll see if
I get to it. And a disclaimer, this is a work in progress. So some of this may not be completely flushed
out, and experiments are a little bit preliminary. So be free to ask any questions. And if you have
feedback, even better, that would also be great.
Okay. Maybe won't spend too much time on the introduction. We don't have too much people in the
audience. So yeah, this is a pretty well-known graph that illustrates how hardware we're scaling
starting from 70s to 2010. And clearly in the first few decades, this is the log scale, so everything was
kind of growing exponentially, which really spurred this computing evolution that was happening
around the time. And Microsoft certainly profited from this group, and other companies as well.
And then around, you know, 2004, '05, certain things started slowing down, in particular clock speed,
that we were, upon which improvements we were relying on before. And that's when companies such
as Intel decided to switch to multi-core processers.
And today, you know, this free lunch that we've been all enjoying for many years, it's pretty much over.
So what broke around 2004 was in our scaling, which was saying that as transistors get smaller, their
power density does not change. So you can keep increasing frequency. And then like I said, that's
when hardware companies decided to switch to multi-core, which is, you know, it kind of worked. But
it is a partial solution, because you have to then write parallel programs and so on, which is not always
easy as, you know, just spitting out new chips, sequential execution on your chips.
And then Moore's law, which talks about doubling of transistors that hardware manufactures can put on
chips, it seems that it's slowing down as well. So there is some recently news that Intel is pointing their
animater chip-making process. And on top of that, you can't really power up all the transistors that you
have on chips these days. So there's this constant of dark silicon that talks about which transistors can
be powered up and which not and so on. So we have to be careful about that as well.
So this is kind of the way of the land of what is happening in hardware these days. Now if we look at
applications that are really popular these days, we can see that lots of them are, I said, fuzzy. Because
they thought maybe to take this fuzzy or we don't need really totally precise answers and so on. And
there is many applications that fall into this category arranging from machine learning and big-data
analytics to cyber-physical systems, graphics, and so on, games, sensors in mobile devices. So these
are all applications that we potentially don't need completely correct results because the deal with
fuzziness of the world around us anyways.
And, you know, this slow down of advances in hardware combined with this fuzziness of applications
that we want to run on this hardware has led to emergence of approximate computing. And the main
idea about it behind it is to embrace this fuzziness by introducing controlled trade-off of accuracy to
improve performance or energy efficiency. So instead of performing completely correct computations,
maybe relax them a little bit, which can cause the [indiscernible] improved performance or energy
efficiency. But they're still happy with the results. So that's the main kind of idea behind approximate
computing in my opinion. And it's been an up-and-coming area in the last couple of years; there's been
a bunch of papers in this area. And two years ago, a year and a half ago, we also started working on
some of the problems in this area, which is quite interesting.
So there is a bunch of ways that approximations can be introduced. At least in some of them there is
more than this. Just to give you some intuition of what these approximations are. So according to
mine, for example, is working on approximating memories so they're playing with tuning memory
controllers to be able to lower refresh rate in DRAMs. So that way you preserve energy, but you might
get more bit flips in the [indiscernible] that you read and so on. So there is a trade-off there.
Then there were papers on relaxing loop computations by doing loop perforations, which means you
just keep every few iterations of the loop. Recently there has been work on neural processing units
where you train your neural metric to replace a part of your problem and so on. So those are kind of
approximations that people have been working on.
And then motivation for behind our work is that introducing these approximations has to be done in a
controlled way ideally. Because there is this fine line between overly aggressively approximating code,
which can lead to then really bad results in the output or being very careful in doing just very
conservative approximations, and then you don't really benefit a lot from them. And to facilitate this, I
think the programmers need automated techniques and tools to explore different approximate
computing trade-offs so that they can play with these different approximations and see what is good
enough for their purpose.
So in the literature, people cannot split up this criteria based on which you judge whether
approximation is good or not into a couple of different categories. So first one is, and the most basic
one, is safety. So safety just says that approximations would hopefully not introduce any catastrophic
failures. And I'll point to the references or things like that.
The second one, and that one hasn't actually been mentioned a lot in the later work, is termination. So
ideally the approximations that they're getting introduced should not cause non-termination.
And finally we have quality results. And this is again typically split into two subcategories. The first
one is accuracy, which talks about difference between precise and approximate outputs. For example,
an approximate output is at most five away from your precise output. And the second one is reliability,
which talks about probability that approximate output is precise. Some example would be my
approximate output is correct 90 percent of the time. And in the other cases it might not be correct.
And so far our work has focused on these three criteria. So on safety, termination, and accuracy. We
still haven't really touched reliability. That's potential future work, future work area.
Okay. So in our goal, like I said, is to enable this rigorous exploration of approximate computing tradeoffs. And we want to do this using automated formal techniques for reasoning about approximations.
And we decided to do that after we studied a little of the literature. We know that current techniques
that people use often lack in either rigor, because what often happens is that to assess approximations,
other researchers just do relatively simple dynamic analysis of sampling. So there is no really any kind
of rigorous proofs that the approximation is really good enough. Or some lack precision, where
something like a type system can limit what you can really express and what you can reason about
when it comes to approximations.
And finally recently there were several techniques that improved precision and so on, but they suffer
from not really that much automated, because they heavily relied on interactive theorem provers, and in
particular it's Coq.
And we want to apply automated differential program verification for reasoning about these
approximations. And the idea is to compare original and approximate program. While at the same
time we encode the relaxed specifications as differential assertions. So this property I was talking
about, safety, accuracy, and so on, can be accorded as assertions that talk about precise and
approximate programs.
And I'm sure you've seen talks about differential program verification before from Shuvendu. It seems
similar to checking for hardware. But it goes beyond the clear lines, which is why it's nice for
approximate computing where what you're trying to prove is not [indiscernible].
And our hope is that we will achieve precision and automation using SMT-based checking and
invariant inference. We know SMT well, as well as invariant inference. So we hope that this work will
improve up on something like using Coq.
So this was brief motivation. What I wanted to talk about next is how we verify safety and accuracy
criteria. And we do it using differential verification, which is based on mutual summaries that relate
pre- and post-states of two-procedure versions. And Shuvendu published algorithms that can check
these mutual summaries modularly by constructing this product program. And by doing that you can
use any off-the-shelf program verifier or inference engine, which is really nice, so we can leverage
Boogie and Houdini and Z3 and apply all of that technology on to check this criteria that I listed for
various approximations that people care about.
I don't know if I should really talk about mutual summaries and things like that. How many people
have seen these talks before?
Okay.
>>: [Indiscernible].
Zvonimir Rakamaric: Okay. So let's try to briefly go through it. So mutual summaries is a general
form of writing differential specification. So if you have two versions of a procedure, then you can
write the mutual summary which relates inputs to outputs of both of these procedures. So this mutual
summary is saying if Xs are the same and these global variable G in one version is equal to G in the
other version and X1 is equal to 0, then the global of the output is 1 is less than equal to the other one.
So it connects these two procedures, and it specifies, it talks about a specification of these two
procedures. And, you know, it includes parameters of both of them and globals on the input and output
and also return value.
So these mutual summaries are used in the algorithm called differential assertion checking, called
DAC, which enables module reasoning and invariant inference. And the steps of DAC are to convert
the differential verification problem into a single program verification problem. So you have these two
program versions and you smash them together into a single program. And you have to do it in a very
strict and prescribed way. And once you have this product program, then you can leverage any single
program verification technique to infer invariants. And once you infer the variants, you can just verify
this combined program using anything you have that can check single programs.
So now I'm certainly not going into this. But you can find it in Shuvendu's paper. So this is the
construction of the product program. So you have two program versions that are taken as input. In this
case they're just two procedures, f1 and f2. And which invoke h1 and h2. And this is the product
procedure for these two procedures. So there are a couple of things to note. It's one procedure that
captures the facts of both of these. It performs these calls to h1 and h2. Because this body of this
procedures are being in-lined here. And another important thing is that in the end it also calls product
procedure for h1 and h2.
So this is kind of the key point, because now you can stick in those mutual summaries at these call
places and you can actually prove them and so on. Without this you can't really do that and then you
cannot infer pre- and post-conditions. So you cannot basically perform mutual summaries. So yeah,
key part of the translation is I think this, and there is kind of a lot of bookkeeping around it, which I'm
not going to go into. But this is how the product construction works. And then once you have that, you
know, you can run something like Houdini for a change to infer mutual summaries.
Okay. And ergo, this is [indiscernible] to do. We can do invariant inference. It exploits structural
similarities between two programs by just restricting the kinds of variant we can generate to simple
differential predicates. And the differential predicates that it means that it talk about variable versions
from the two programs. So they're in the form of X and then some operator X prime where X is from
one program version and X prime from the other one. And this operator can be simple things such as a
[indiscernible] and so on.
So these are the predicates or predicate templates. And then we use Houdini to infer Boolean
combination of these predicates. And this happens completely automatically once these predicates are
provided, or predicate templates are provided.
And I should also note that SymDiff automatically generates a bunch of these. So it don't even, for
many examples it don't even have to go in and provide any kind of templates, but if you need
something more complicated, you're able to help it out a little bit.
Okay. Yeah, the key benefit of this is that you can reuse the infrastructure. So once you create the
product program, this box here is what you already have. So this is, for example, Boogie and Houdini.
Which is really nice, you don't have to develop really anything special for discharging these product
programs. And we leverage this a lot in our work.
So the implementation of our tool that discharges this approximate programs and their differential
properties is based on this encoding. We did have to extend it with automatic inference of richer
invariants in the sense that users can specify additional predicates, and we can infer Boolean
combination over predicates and not just conjunction. So that is some of our examples. It gets a little
more complicated than what SymDiff has seen before.
Okay. So this is tool flow. There's an original program and an approximate program that is fed into
SymDiff. Potentially we need a few manual predicate templates. From that the product will be
generated, and then Boogie and Houdini are run on it, and hopefully it can be discharged using that.
So like I said, we started out verifying two criteria. One is safety. And in our particular case what we
checked is control flow equivalence. Which basically means that for the same input, both programs
execute the same sequence of basic blocks. And this criteria is actually quite important when
introducing new approximations, because you often don't want to break this guarantee. Typically it
means that weird things are happening if your two versions are not control flow equivalent. And this is
mentioned in other related work papers as an important thing to work on.
And we also have a couple benchmarks to check accuracy. And in this case this accuracy criteria came
from related work that used the same set of benchmarks. And one example would be that output
computed by approximate procedure is within some prescribed distance from the original procedure
output.
Okay. A little about control flow equivalence. So we came up with this, I think, pretty, pretty clear
way of encoding control flow equivalence using uninterpreted functions. So we have this global
variable called summary, and in every block we updated with the new value, which is just F of
summary and block ID, where F is some uninterpreted function. And for every block we generate you
need block IDs. And by doing this, in the end we just have to compare the summary variables in the
two versions. And that actually works really well, and I think it's a quite nice encoding of controlled
flow equivalence. I haven't seen it before. It's pretty simple, but pretty nice.
There is an old [indiscernible] with actually storing things in an array. You know every block was
stored in unbounded array, which was kind of pretty dumb and not scalable. So this was a nice kind of
object to that.
>>: So when you look at the invariance and you're just saying things like summary on the left equals
summary on the right or something like that. So you don't have to worry about the functions
[indiscernible].
Zvonimir Rakamaric: Yes.
>>: [Indiscernible].
Zvonimir Rakamaric: What?
>>: You said approximation [indiscernible].
Zvonimir Rakamaric: Yeah, so let me show you, that will come up soon. Yeah, I didn't put it in this
example, but they'll come up soon. Yeah. Good question though.
Here is a good example. And next slide we have [indiscernible]. So we have a couple example where
we check control flow equivalence. This is one of them. So the main thing to focus on is this part. So
this is just a recursive procedure that iterates over this array, global area, and it searches for a character
that is equal to X and then if it is then it replace it with Y. So it's basically replacing X with Y in this
array. And it terminates when either N is reached or array is set to minus 1. So it's your basic replace
character procedure.
We do have to turn everything into tailored cursive procedures. So you would usually write this using a
simple loop, but here we encoded it using recursion. So that's the original version.
And approximation basically scrambles whatever was ready from this array. So this is the havoc that
introduced approximation. So we just whatever is read in from an array we completely scramble it. So
we have two versions now, one without havoc and one with havoc, and we want to prove that
controlled flow equivalence, that they're controlled flow equivalent. So that's our goal.
>>: That's an interesting [indiscernible], right? I mean you have the [indiscernible] there and you can
consider control flow [indiscernible].
Zvonimir Rakamaric: That is a very good question. No.
>>: [Indiscernible].
Zvonimir Rakamaric: But very good question. So, yeah, no, we don't [indiscernible]. We do count
this. Yeah, good question.
And so yeah, what is kind of interesting about this example, it's kind of not trivial to do it, it's not easy
to do it using just simple tent analogies. So if you notice here, you know, this scrambled variable is
flowing into this array, which then flows back into this F condition. So we tried simple tent analogies
on this and it will just say everything is tented. And this happens because it can't really reason about
precisely every segment. You can imagine extending some kind of additional abstract domain that
would kind of keep track of actual array segments and so on, but it gets really tricky. And using
SymDiff and so on, we can actually do things like this.
>>: Can you mention what this analogous [indiscernible]?
Zvonimir Rakamaric: So, yeah. We use this Boogie program. So I sometimes forget to relay this to
real world. That's what happens when you work in program metrics. So it's trying to mimic reading
from approximate memory. So every stored approximate memory you read from it and you can get
anything essentially.
So that's one kind of real example. That's probably our smallest one. So we verify this control flow
equivalence with this example using SymDiff [indiscernible] I showed you. And verification effort is
almost completely automatic. We have to just manually provide one relatively simple predicate
template, which is actually talking about these array fragments. You kind of have to know whether
you're ahead of I or not. So that's the predicate we needed.
So that's example where we check the safety criteria. Switching to accuracy, we took a few examples
from this PLDI 2012 paper that was proving this acceptability properties for approximate programs
using Coq. And these are pretty small examples. I guess when you're using Coq you can't really, well
okay.
So here's one other examples. It's highly abstracted search engine called Switch, and this procedure is
supposed to print out on the side how many search results should be printed on the screen. So it takes
this maxR and N, and numR is the number of search results. So it has this loop where it's either bonded
by maxR or N. And then just keeps [indiscernible] and then it returns numR. So very, very simple and
abstracted abstract version of something that you could maybe see in a search engine.
And then here is the approximation. So the red lines are what uses this approximation. It's a little bit
more involved and interesting than what we're heading for. So we are not just completely scrambling
this maxR variable, but rather we do this in a more controlled way. So what this is saying is actually
that if maxR is less than 10, then don't change it. And if it's greater than 10, then it can be anything as
long as it's still greater than 10.
And the idea behind this approximation is that the server can kind of throttle how many things, how
many searches it's presenting to the user. So if the load becomes really high, then it can, instead of
presenting 100 results, it can present 20 results. And you always have to present a list then. So that's
the idea behind this approximation. And it was taken from that paper.
So we have to also specify what we actually want to check. And this is an example of this bit distance
kind of criteria where we are saying the input are the same, then the output should be related by the
same relaxedEq function.
Okay. So a little bit other verification effort again. So we managed [indiscernible] this by providing
manually 4 [indiscernible] simple predicates. Carbin et al had this proofing Coq that had around 330
lines of proof script. So, you know, the examples are pretty small. But I think we really are kind of
improving up on this. And the surprising thing is, also, and I discuss this with Mike. I mean we are
proving exactly the same properties. So it's not that we changed anything or removed anything.
Exactly the same example, exactly the same property. And we can do it pretty automated free.
>>: [Indiscernible] one of the tricky things that Mike had to deal with was re-convergent control.
[Indiscernible] cases where you do control flow is [indiscernible], actually you come back to the same
place and you want to say something about the values that exist in both programs at the same time.
[indiscernible].
Zvonimir Rakamaric: So I haven't seen the examples or actually have that. Like maybe it's a different
paper. The examples in this paper are all kind of following this paper, and it's not control flow.
>>: If they will build the proof necessary for it but haven't found any example to exercise it.
Zvonimir Rakamaric: I will think about this. It's not, so you're saying he won't allow the divergence in
a certain part and then it converges back?
>>: [Indiscernible] reasonable thing to say like X1 and X2. It might be the product of a totally
different control. But now you're at the same place and the same variables are available. I see.
Zvonimir Rakamaric: I mean we can play certainly with where you want to track it and where you
don't want to track it by putting these updates to summary or not. So I'm not, maybe. We can think
about it. That's a good question.
So yeah, we do have a little bit more extensive relation than just these two examples. And we have a
couple more control flow equivalence.
Selection sort is pretty interesting, because it has some of the similar character, replace character.
There is this [indiscernible] array that don't really influence control flow. And the key to be able to
handle these examples is that you need to precisely capture these array fragments. So we're kind of
focusing on examples where doing just simple tent analysis is just not good enough.
And we handled other examples from this Carbin's paper, there's three of them.
>>: [Indiscernible].
Zvonimir Rakamaric: It was always just held. [Indiscernible]
>>: [Indiscernible].
Zvonimir Rakamaric: Yes.
>>: [Indiscernible].
Zvonimir Rakamaric: So I remember, I probably, I actually remember selection sort. So selection sort
kind of, oh, no in selection sort it actually right. It's right. It kind of has this index going from end of
the array towards the beginning of the array. And control flow only depends on the, and everything
after the index is sorted.
>>: Yeah but you have this condition somewhere that says if x plus Y then [indiscernible].
Zvonimir Rakamaric: Yes, but this condition only talks about the elements that are before I. You go
from 0 to I and then you, that's why you're applying this condition. Once you are behind I, that doesn't
influence control flow anymore. So basically when you are writing your largest element at I, you can
actually write anything you want. You can write completely scrambled value, so I will move and then
your control gets preserved.
>>: [Indiscernible].
Zvonimir Rakamaric: I can't remember how Bubble Sort. I think it was sort of like the similar
behavior. It depends how you implement it. I think we just have two loops.
>>: [Indiscernible].
>>: It seems like in Bubble Sort if you're just testing and swapping, right, and if that's [indiscernible],
then you have a different control flow pattern and the value of variance.
>>: I guess Bubble Sort you always have that loop, you have the nested loop, and then [indiscernible]
depends on the value, but the control flow is the same it's always going to be exactly.
Zvonimir Rakamaric: But we can't swap it actually. I mean it is control flow. It is. Because it is less
than swap or not swap. So this kind of is control flow. Do you know [indiscernible] maybe?
>>: So you're not always interested in fully preserving control flow. [Indiscernible] I don't care which
way. So these are somehow to specify at what level of granularity.
Zvonimir Rakamaric: So [indiscernible] but I think before so we can actually, given our coding we can
pretty easily kind of based on some kind of user input either imitate the blocks with those other
summaries or we don't annotate them. If you don't annotate them then those are just analyzed. So you
can just put these control flow tracking updates to key points in the program. And everything else will
still work.
>>: [Indiscernible].
Zvonimir Rakamaric: Yeah, so we can do something like that. It's approximate and flows into the flow
[indiscernible].
And here are some experiment results. So I managed to verify all of these. And this is the actual
verification. Our initial effort was actually on rolling, which is kind of much easier, but we were not
happy with that. So here we actually generate invariance and perform like full-blown verification.
Okay. So let's talk a little bit about relative termination. And this is one I'm particularly excited about.
And we only started working on this maybe two, three months ago. But, yeah, we're getting some
direction and I think it's very interesting direction. So this termination criteria, people don't really talk
about it that much in the approximate computing community. Although if we talk to somebody
[indiscernible], people think that it is important.
And so the way we specify it is that two programs satisfy this relative termination criterion. If a precise
execution terminates for a given input, then it's approximate version should terminate as well. So it's
not absolute termination. If your precise version does not terminate, then you don't care what your
approximate version does.
And there are some advantages to doing things this way. First of all, we avoid proving absolute
termination. I believe that proving absolute termination is typically easier, because the kind of
invariance you need, and I have one example, they're actually not that involved often. And we know
that coming up with good ranking functions can be quite hard if you want to prove absolute termination
in a single program.
And I also do think that it is more meaningful in the context of these program approximations, because
typically you want a reason for two program versions and you want to see how your approximation
influences the original version. So I think it's probably a more meaningful thing to check when it
comes to approximations and so on.
>>: [Indiscernible]
Zvonimir Rakamaric: No, I should have said that. I'm missing, that's a great question. So everything
that I talked about so far assumes that they terminate. So this is actually, another advantage that I
should adhere, another advantage I should adhere is it actually assures that whatever you prove in the
previous step really holds, if you can prove absolute termination.
>>: If you test that that summary is right on termination, [indiscernible].
Zvonimir Rakamaric: If you go somewhere and you don't even get to the actual assertions and so on,
so you don't prove. This was very good. I should have had this sentence in the slide.
So yeah, another great advantage of this.
>>: Has your mother-in-law seen this slide?
Zvonimir Rakamaric: My mother-in-law? No.
>>: I'm sorry, the first, I mean relative safety and relative termination.
>>: I'm sorry [indiscernible].
[laughter]
Zvonimir Rakamaric: [Indiscernible].
>>: [Indiscernible].
Zvonimir Rakamaric: I love my mother-in-law. And she's visiting my wife right now actually. Which
is also great so I get a chance to travel more. [indiscernible]
>>: I can take the blame. So neutral termination [indiscernible]
Zvonimir Rakamaric: [Indiscernible].
>>: [Indiscernible].
Zvonimir Rakamaric: [Indiscernible].
[laughter]
Yeah, so the construction for proving this termination of reiteration is kind of somewhat similar to
product program construction that I already talked about. Instead of having mutual summaries, you
have relative termination condition that enables you to prove a relative termination and then you also
have to check. So this is like pre-condition.
So this checking is based on product program coding that was published in a CADE 2012, '11, with
[indiscernible] and a bunch of people published it. And it's a pretty neat encoding for checking this
relative determination. So you have to introduce these uninterpreted summary relations that capture
input-output behavior functions. And then you assume that nested calls that are being made, you know,
recursive calls or nested calls, you assume that they terminate. And you can do that using axioms that
refer to the relative termination conditions I talked about in the previous slide and these summary
relations. And then you speak of assertions that callees also terminate. And this assertion relies on this
assumption.
So let's just see a little bit how the construction looks like. So this is a very simple example, simple
[indiscernible], increases X by 1. This is approximation. It's kind of what [indiscernible]. Instead of
increasing by 1, you increase by 2. You skip every other step.
So I want to prove that this approximate version relatively terminates as compared to the original
version. And the first thing you have to do is extract loops [indiscernible] procedures. So this is the
main kind of part and here is the recursive call. So nothing special really happening there. And we do
have to, so these are the summary relations. So they're being assumed at these two places. So this one
belongs to loop, that one belongs to main. And the main thing we want to prove is estimation of loop.
Okay. So here is how the construction works. So this is your product procedure where you inline the
body of your first function and you inline the first procedure and the body of your second procedure.
>>: You mean X1 plus 2, right?
Zvonimir Rakamaric: No, X2 plus 2.
>>: Plus 2.
Zvonimir Rakamaric: Yeah, sorry, so this is a typo. So this should be the body of the second
procedure, so it should be X2 plus 2, X2 plus 2.
And you have to assume your relative termination condition. And in this case it's that X1 has to be
lesser or equal to X2. Write [indiscernible] so if X2 is less than, it could happen that the first program
exits right away, but the second one has to iterate.
And this is this axiom that I was talking about. So this axiom is saying that if relative termination
condition holds and loop 1 terminated, then there should exist r2 such that loop r2 closer terminates.
And this is encoded using this summary relations.
>>: [Indiscernible].
Zvonimir Rakamaric: Yeah, because this is assumption. And you check here. So there is, I guess,
induction happening here. And it took me a while to actually understand why this actually works. But,
yeah, so this is saying your nested calls are terminating and then you want to prove that at the current
level you are terminating as well. Does that make sense?
>>: So [indiscernible]?
Zvonimir Rakamaric: So this assertion here proves that you're terminating at this level. So this is
important. Any other questions?
>>: [Indiscernible] means that it possibly terminates on input X with output X and output on r1 over
here?
Zvonimir Rakamaric: Right.
>>: So you're proving there is some execution from X1.
Zvonimir Rakamaric: So yeah.
>>: So does that mean also that loopR2 could be also possibly non-terminating?
Zvonimir Rakamaric: If loop R1 terminates, then loop R2 that's terminate as well.
>>: At least if you execute [indiscernible].
>>: So if loop R1 is possibly terminating, then loop R2 is possibly terminating. [indiscernible]
Zvonimir Rakamaric: Oh, I see. Okay.
>>: I just wanted to understand this.
>>: [Indiscernible] R1 is deterministic.
Zvonimir Rakamaric: Anyhow, [indiscernible] there is a lot of multipliers though which can get tricky.
Okay. So ->>: The only one that's kicking there is the assertion.
Zvonimir Rakamaric: Yeah, okay. Sure.
>>: [Indiscernible].
Zvonimir Rakamaric: We can prove many of some examples but then some other ones gets a little bit
harder. And I worked with [indiscernible] a long time ago and I never wanted to work with him again.
Okay. So yeah, so now we have this kind of dual holdings, right. We have the big one where you are
actually in walking these combined procedures and where you can incur invariance. And we have this
CADE encoding we just showed that we can use to prove termination. But it's not really clear how to
do invariant inference here because this product procedures are actually not being invoked anywhere.
Instead we have these axioms. So to be able to do automated verification in this context then we came
up with a solution which iterates between these two encodings. So we generate invariance using DAC
encoding and then we transfer them into CADE encoding, and we check for termination, and then we
can kind of iterate this. So look at it two-fold. Very similar except now we generate two product
programs by using both encodings and then we pushing invariance from one into the other and then try
to prove termination. So [indiscernible] implemented this part.
And we've been playing with this a little bit. We have a preliminary relation. I think we have two or
three examples. So this perforation that I showed and I replaced character. Same example, we just
proved termination. And it's pretty automated. Again, we only need one or two manually provided
predicate templates. Quantifiers are kind of tricky. And we do see non-termination and things like that.
And we've been working on this in the next few days.
So that's termination. I mean I like it in the approximate kind of computing community there hasn't
really been a lot of effort on doing anything like this. So I think it's pretty new and we'll see how far
we can push it.
And I'll skip floating points in the interest of time. We have some basic work on synthesizing floating
point routines with lower bit of floating preparations and [indiscernible] and so on. Which I think it's
quite promising. You can say [indiscernible] and date bend it and things like that. And we do it in a
rigorous way using our tool that we have for reasoning about array of floating computations and so on.
And like I said, I can talk about it offline.
And yeah, about this work. So we show that we can apply this automated differential program
verification as a SymDiff for reasoning about approximations. We also showed that these mutual
summaries that I talked about can quite naturally express several types of relaxed specifications that are
useful for proving in this context. And we show that we can improve precision and automation. Our
previous approach is because using SMT checking and invariant inference, which is almost completely
automatic. And by combining all of this, we proved that it is feasible to apply all this verification
techniques that we are all familiar with and that we all love, or most of us do at least, to the domain of
approximate computing.
So I think I ran out of time. So I'll stop here. And like I said I can talk about floating points offline if
anybody's interested. Thanks a lot guys.
[Applause].
Any other questions?
>>: Just need some more examples.
Zvonimir Rakamaric: [Indiscernible] and the first point is of more interest. Yeah, benchmarks are
quite challenging. We did get some from literature and [indiscernible] has been helping us a lot to try
to find more benchmarks. So hopefully in the next week or two our benchmark set will be much larger.
And then we can always try to improve predicate degeneration further and so on, count the individual
stuff.
Anything else?
>>: [Indiscernible].
Zvonimir Rakamaric: Thanks a lot for coming.
[Applause]
Download