annatala wolf – 2231 lecture 1 reading mathematical contracts

31
ANNATALA WOLF – 2231 LECTURE 1 Reading Mathematical Contracts

Upload: ryan-jester

Post on 13-Dec-2015

217 views

Category:

Documents


0 download

TRANSCRIPT

ANNATALA WOLF – 2231 LECTURE 1

Reading Mathematical Contracts

Contracts (specs) ≠ Code!

It’s easy to get contracts and code confused because we use a strict language for both of them. In contracts, we use mathematical notation. In code, we use Java syntax and grammar.

Often, the two coincide. For example, -18 or false mean the same thing in both contracts and code.

But sometimes, the two do not coincide! In contracts, { -1/2 } refers to the set { -½ }. In code, { -1/2 } is likely an array initialization statement,

which produces this string of integer: < 0 >. Notice that even the value of the string’s entry is different (think about why).

Specifications vs. Code

Since our specs are written in a formal and precise manner, you might wonder how they differ from code. In fact, they are another form of code.

The difference is that specifications describe what code does, not how it does it. Specifications can be more abstract than code.

Consider the relational behavior of the removeAny() spec for Set.

One can even write a specification for a problem that can’t be solved at all, like the halting problem.

Why formal (mathematical) contracts?

Formal contracts are the only thing we use in this course that you won’t see anytime soon in industry. Computer scientists are

expected to read math, so the formal specifications we require are appropriate for the course.

The good news is that, unlike English, math is totally unambiguous.

The bad news? You have to learn how to read this shit.

Our Contracts (Review)

Preconditions are given in our custom @requires annotations, and postconditions are given in our custom @ensures annotations.

The client (user) of an operation is told only one thing: if they meet the precondition, the operation guarantees the postcondition will hold. If not, any behavior is permitted, including non-termination.

The implementer should not check preconditions, because the contract does not need to be fulfilled. We use asserts to help the developer during design (they are removed from the final product).

Who does what?

In design by contract (DBC), remember:

Failing to follow any one of these directives shows you don’t understand DBC, and will cost you points.

Client Implementer

precondition(@requires) must check

should assume

postcondition(@ensures)

should assume

mustmake true

Mathematical Types

Every object or primitive type we use is modeled by an underlying mathematical type: Boolean: either true or false integer: a positive or negative whole number character: a single Unicode character, like ‘@’ or ‘\t’ set: an unordered collection of items

(sets have no order and don’t maintain/understand duplicates)

string: a totally-ordered collection of items (not Java String) tuple: a group of different things that can be distinguished

(like the columns of a database)

Examples of Mathematical Models

String: string of characterNaturalNumber: non-negative integerQueue<T>: string of TStack<T>: string of TSequence<T>: string of TSet<T>: finite set of TMap<K, V>: partial function of K

V (you can think of this as a finite set of pairs (i.e. 2-

tuples) (K,V), where each K value is associated with at most one V)

Use correct math notation!

The mathematical notation we use is very precise.

If you don’t usethe correct notationon exams or otherassignments, youcan lose significantpoints – so makesure you know howto do this.

Mathematical Notation

Sets use curly braces: {a, b, …}. The set with no items, { }, is called the empty set.

Strings use angle brackets: <a, b, …>. The string with no items, < >, is called the empty string.

A string or set with a single element, such as <x>, is not the same as the element x by itself.

Tuples use parentheses: (a, b, …). A tuple may contain different types. Order identifies which item is which, but the order chosen is arbitrary. A 2-tuple is often called a pair, a 3-tuple a triple, etc.

More Symbols

The * symbol means concatenation when it appears between two strings. It means multiply when it appears between two integers. There is never ambiguity, because we always specify types.

The entries of a string are all the unique bits of the string, taken as a set. (Remove order and forget duplicates, in other words.)

A permutation of a string means a string with all the same elements, in possibly a different order.

A string of character (like <‘H’, ‘i’, ‘!’>) can be abbreviated using double quotes (like “Hi!”).

One Last Symbol

The parallel lines |x| can mean three different things, depending on context: x is a set implies |x| is the size of the set x is a string implies |x| is the length of the string x is a numeric type implies |x| is the absolute value

It may seem like “size of a set” and “length of a string” are the same thing, but mathematically the concepts are different. The idea is very similar for finite sets and strings, though: it just counts the number of items (though don’t forget: duplicates are ignored in sets, but differentiated in strings).

Understanding Quantifiers

TODO (will explain quantification in detail here, probably with two or three slides)

Reading Specifications

/** * @replaces s2 * @requires * |s1| > 0 * @ensures * |s2| = |s1| - 1 and * for all i, j: integer, a, b: string of integer * where (s1 = a * <i> * <j> * b) * (there exists c, d: string of integer * (|c| = |a| and s2 = c * <(i+j)/2> * d)) */

public static void smooth(Sequence<Integer> s1, Sequence<Integer> s2) {...}

Breaking The Specification Down

Sequence is modeled by string of T (in this case Integer), so s1 and s2 are type “string of integer” in this specification.

|s2| = |s1| - 1 and

for all i, j: integer, a, b: string of integer where (s1 = a * <i> * <j> * b) (there exists c, d: string of integer (|c| = |a| and (s2 = c * <(i+j)/2> * d))

Part 1

Part 2

The easy part

@requires |s1| > 0 @ensures |s2| = |s1| - 1 ...This tells us s2 will be one item shorter than s1.

It makes sense because s1 can’t be empty to start.

Clearly, if s1 has one item in it, s2 must become the empty string of integer. But for longer s1 values, we need more information.

Since s2 is replaces-mode, its incoming value should not affect the result. The values that appear in s2 must come from s1 or from the contract itself.

Quantified Strings

When we model strings of items, frequently we will want to talk about what happens throughout the entire string. In this case, we are talking about all possible ways that you can arrange s1 so that there is an <i> next to a <j>. In other words, “consider all consecutive pairs of integers i and

j, in s1”.

The strings a and b are merely placeholders. It doesn’t matter what they contain—they’re just there so we can say “let’s talk about each consecutive pair of integers in s1”.

for all i, j: integer, a, b: string of integer where (s1 = a * <i> * <j> * b) ...

“Vacuously True”

If no integers and strings canfit the for all quantifier, then there are no cases for us to test. This makes the statement vacuously true.

This could only happen when |s1| < 2. But we already know what happens when |s1| = 1, and |s1| can’t be 0, so this shouldn’t cause a problem.

for all i, j: integer, a, b: string of integer where (s1 = a * <i> * <j> * b) ...

we need at least two integers in s1 for this case to even occur

Quantifiers: There Exists

With “there exists”, we’re making some assertion about the state of an object. Here, we say what part of the value of s2 must be, based on s1.

there exists c, d: string of integer(|c| = |a| and s2 = c * <(i+j)/2> * d)

this part tells us what we should find in s2

this part tells us where it should be found in s2

What are the values?

The values in s2 are just the smoothed (averaged) values of each pair of integers. This is why s2 is one shorter than s1. For example, if there are six integers in s1, there are only five consecutive pairs.

s2 = c * <(i+j)/2> * d

each integer in s2 is the average of two consecutive

integers from s1

Where do the values go?

By adding some information about those placeholder string variables, we can describe exactly how the two strings match up.

Can you see how they line up?(Hint: Just ignore b and d!) s1 = a * <i> * <j> * b s2 = c * <(i+j)/2> * d |c| = |a|

The Meaning of smooth( )

This procedure averages the consecutive values of s1, and produces s2 with the averaged values in the same order in which the pairs appeared in s1.

This spec is a good example because it’s just about the hardest thing you will be expected to read and interpret on an exam. Don’t let the quantifiers scare you. Most of the “string” variables we create with quantifiers are only there as placeholders, so we can talk about multiple cases at the same time.

Recursively-Defined Specifications

/** updates seq requires (|seq| mod 2) = 0 ensures seq = SWITCH(#seq)

math_function SWITCH(string of integer s): string of integer if |s| = 0 then SWITCH(s) = empty string else if |s| = 2 then there exists a, b: integer where (s = <a> * <b> and SWITCH = <b> * <a>) else there exists a, b: integer; t: string of integer where (s = <a> * <b> * t and SWITCH(s) = <b> * <a> * SWITCH(t))!*/public static method weird(Sequence<Integer> seq) { ... }

A math_function is just a piece of a specification. It is used to make specs easier to read. It is not code. You can’t make a “call” to SWITCH(s).

The Specification for Weird( )

requires

(|seq| mod 2) = 0

The sequence must contain an even number of integer elements.

ensures

seq = SWITCH(#seq)

Note that #seq means “the incoming value of seq’s object”.

The weird() method will update the sequence object by doing whatever SWITCH describes.

Interpreting SWITCH(s)

SWITCH changes one string of integer into a different string of integer.

math_function SWITCH(string of integer s): string of integer

if |s| = 0 then SWITCH(s) = empty string

else if |s| = 2 then there exists a, b: integer

where (s = <a> * <b> and SWITCH(s) = <b> * <a>)

else

there exists a, b: integer; t: string of integer

where (s = <a> * <b> * t and

SWITCH(s) = <b> * <a> * SWITCH(t))

case 1: |s| = 0

case 2:|s| = 2

case 3:“other”

SWITCH(s) Case 1

If you SWITCH(< >), it returns < >. In other words, it does not change s when s is the empty string of integer.

if |s| = 0 then SWITCH(s) = empty string

SWITCH(s) Case 2

If s has two elements, SWITCH(s) returns a string consisting of those same two integer elements, in reverse order.

else if |s| = 2 then

there exists a, b: integer where

(s = <a> * <b> and

SWITCH(s) = <b> * <a>)

Limiting Complexity

SWITCH(s) doesn’t make any sense at all when the length of s is odd. But that’s okay, because we’re only using it to describe what happens when its length is even.

In other words, we don’t need to bother reasoning about cases that won’t come up.

SWITCH(s) Case 3

If s has more than two elements, we call the first two a and b. We reverse these two, then recurse to append SWITCH(t), where t is the rest of the string. It’s safe, since |s| is even implies |s|-2 is even.

else there exists a, b: integer; t: string of integer where (s = <a> * <b> * t and SWITCH(s) = <b> * <a> * SWITCH(t))

The Meaning of SWITCH(s)

Given a string of integer of even length, SWITCH takes every two integers and swaps them, like so:

SWITCH(< 1, 2, 3, 4, 5, 6 >) =< 2, 1, 4, 3, 6, 5 >

Note: just because a spec is recursive doesn’t mean you need to use recursion to write code that implements it. This method would be both simpler and more efficient if written iteratively.

Really Hard Contracts…

TODO (will add Project 3 contract reading stuff here)