CS195Y Lecture 11

2/24/16

Announcements:

Transitive Closure Continued

Let’s look at a similar example to the graph from last week.

sig City {roads : set City}
pred allcities[r : univ->univ] {
    r in City->City
}

run { some r: univ->univ | not allcities[r] } for exactly 6

The allcities predicate models whether the graph is complete (an edge from each city to every other city).

Note: beware of declaring things to be of type univ. It’s better to be explicit with what type you mean, especially when your model may change.

So, if I drive from Providence to Boston, usually I can drive back the other way. It would be nice to add to our spec that roads must be symmetric.

fact roadsSymm {
    -- with quantifiers:
    all c1, c2 : City |
        c1->c2 in roads iff c2->c1 in roads
    
    -- relationally:
    roads = ~roads
}

~ is the transpose operator (that flips the columns of the relation). A relation is symmetric if and only if it is identical its transpose, so the above two constraints have the same effect.

We might want to also not allow roads to go nowhere: we should eliminate self-loops.

fact roadsGoSomewhere {
    no roads & iden
}

How can we get the reachability of our roads? That is, how can we tell if we can use the roads to go from one city to another?

pred canDrive[start, end: City] {
    start->end in ^roads
}

Tim hand-waved that the above predicate is symmetric: it doesn’t matter which order you pass in the two cities. Is this true?

assert cdsymm {
    all start, end: City |
        canDrive[start, end] iff canDrive[end, start]
}
check cdsymm for exactly 6 City

No counterexample found! Q: Does this assertion prove that this is universally true?
A: Nope! It proves that canDrive is symmetric for all graphs with exactly 6 cities.

Moving on: I want to take a set of cities, and find out if the set of cities is a cycle.

pred isCycle[cities: set City] {
    cities in cities.^roads -- would this work? No!
}

The challenge with detecting cycles here is that bidirectional edges create cycles when we don’t really mean there to be cycles. Modeling this correctly is a problem on the homework. Refer to the hint on considering individual edges!

What if we wanted all the paths with an even number of hops?

fun evenHops[cities: set City]: City->City {
    let localRoads = (cities <: roads) & (roads <: cities) |
        ^(localRoads.localRoads)
}

Remember that the <: is the domain restriction operator. :> is the range restriction operator. Taking their intersection in the let statements finds all the roads that are only from a road in cities and only to a road in cities.

Another way to get all of the local roads is using the -> operator for the Cartesian product.

localRoads = cities->cities & roads 

localRoads.localRoads gives us all of the hops of length two. Taking the transitive closure of that gives us all the paths that use an even number of hops.

What if we wanted to find all the paths with more than 1 hop?

fun pathsAbove1[cities: set City]: City->City {
    let localRoads = (cities <: roads) & (roads <: cities) |
        ^localRoads - localRoads --Does this work? No!
}

The problem with the above function is that it’s removing length 1 paths that may also be done with a length longer than 1. For example, if you have {A->B, B->C, A->C} as localRoads, this function produces the empty relation when it should produce {A->C}.

fun fixedPathsAbove1[cities: set City]: City->City {
    let localRoads = (cities <: roads) & (roads <: cities) |
        -- union of even len paths and (even len paths).localRoads
        -- note: this is a student suggestion, not tested by Tim
        evenHops[cities] + evenHops[cities].localRoads
        
        -- Tim's approach using a set comprehension:
        {c1, c2: City | c2 in c1.^(localRoads - c1->c2)}
        
        -- Additional student suggestions (Exercise for curious 
        -- reader: check whether these are all equivalent using 
        -- assertions.)
        (^localRoads).localRoads
        ^(^localRoads-localRoads)
}