Transcript Slides

Lecture 7:
A Tale of Two Graphs
CS201j: Engineering Software
University of Virginia
Computer Science
David Evans
http://www.cs.virginia.edu/~evans
Graph ADT
public class Graph {
// OVERVIEW:
//
A Graph is a mutable type that
//
represents an undirected
//
graph. It consists of nodes that are
//
named by Strings, and edges that
//
connect a pair of nodes.
//
A typical Graph is:
A
//
< Nodes, Edges >
//
where
//
Nodes = { n1, n2, …, nm }
//
and
//
Edges = { {from_1, to_1},
//
…, {from_n, to_n} }
B
D
C
Nodes = { A, B, C, D }
Edges = { { A, B }, { A, C },
{ B, C }, { A, D } }
{ … } means its a set – order doesn’t matter
19 September 2002
CS 201J Fall 2002
2
B
D
Representation Ideas
• Set of Nodes, Set of Edges
A
C
e.g., Nodes = { A, B, C, D }
Edges = { <A, B>, <A, C>, <A, D>, <B, C> }
• Set of Nodes and Neighbors
e.g., Graph = { <A, {B, C, D}>, <B, {A, C}>,
<C, {A, B}>, <D, {A}> }
Each entry is pair of node name, and names of
nodes it is connected to.
19 September 2002
CS 201J Fall 2002
3
Representation Ideas
• Set of Nodes and Matrix of booleans
e.g., Nodes = [ A, B, C, D ]
No edge from A to A
Edges = [ [ 0 1 1 1 ]
[1010]
[1100]
[1000]]
B
D
A
C
Edge from B to C
19 September 2002
CS 201J Fall 2002
4
Implementation 1
class Edge {
// OVERVIEW: Record type for representing an edge.
String node1, node 2;
Edge (String n1, String n2) { node1 = n1; node2 = n2; }
}
class Graph {
// OVERVIEW: A Graph is a mutable type that represents an …
Vector nodes; // A Vector of String objects
Vector edges; // A Vector of Edge object
}
…
19 September 2002
CS 201J Fall 2002
5
Rep Invariant
Function from rep to boolean
class Edge {
String node1, node 2;
}
class Graph {
Vector nodes; // A Vector of String objects
Vector edges; // A Vector of Edge object
…}
RI (c) = c.nodes != null && c.edges != null
&& !c.nodes.containsNull && !c.edges.containsNull
&& elements of c.nodes are String objects
&& elements of c.edges are Edge objects
&& no duplicates in c.nodes
&& no duplicates in c.edges
Is this precise
enough?
&& every node mentioned in c.edges is also in c.nodes
19 September 2002
CS 201J Fall 2002
6
Rep Invariant
RI (c) =
&&
&&
&&
c.nodes != null && c.edges != null
!c.nodes.containsNull && !c.edges.containsNull
elements of c.nodes are String objects
elements of c.edges are Edge objects
&& no duplicates in c.nodes
// No duplicate edges, node1/node2 are interchangable:
&& ((c.edges[i].node1 = c.edges[j].node1
&& c.edges[i].node2 = c.edges[j].node2)
|| (c.edges[i].node1 = c.edges[j].node2
&& c.edges[i].node2 = c.edges[j].node1))
 i == j
&& every node mentioned in c.edges is also in c.nodes
19 September 2002
CS 201J Fall 2002
7
Abstraction Function
public class Graph {
// OVERVIEW:
//
A Graph is a mutable type that
//
represents an undirected
//
graph. It consists of nodes that are
//
named by Strings, and edges that
//
connect a pair of nodes.
//
A typical Graph is:
//
< Nodes, Edges >
//
where
//
Nodes = { n1, n2, …, nm }
//
and
//
Edges = { {from_1, to_1},
//
…, {from_n, to_n} }
19 September 2002
• Function from rep
to abstract notion
(use notation from
overview)
AF (c) =
< Nodes, Edges >
where …
CS 201J Fall 2002
8
Abstraction
Function
class Edge {
String node1, node 2;
}
class Graph {
Vector nodes; // A Vector of String objects
Vector edges; // A Vector of Edge object
…}
AF (c) = < Nodes, Edges > where
Nodes = { c.nodes[i] | 0 <= i < c.nodes.size () }
The set of nodes is the elements of the c.nodes Vector
Edges = { { c.edges[i].node1, c.edges[i].node2 }
| 0 <= i < c.edges.size () }
The set of edges is the elements of the c.edges Vector
19 September 2002
CS 201J Fall 2002
9
Implementing
Constructor
class Edge {
String node1, node 2;
}
class Graph {
Vector nodes; // A Vector of String objects
Vector edges; // A Vector of Edge object
…}
public Graph ()
// EFFECTS: Initializes this to a graph with no nodes or
//
}
edges: < {}, {} >.
nodes = new Vector ();
edges = new Vector ();
19 September 2002
How do we know this satisfies the rep invariant?
CS 201J Fall 2002
10
Implementing
addNode
class Edge {
String node1, node 2;
}
class Graph {
Vector nodes; // A Vector of String objects
Vector edges; // A Vector of Edge object
…}
public void addNode (String name) {
// REQUIRES: name is not the name of a node in this
// MODIFIES: this
// EFFECTS: adds a node named name to this:
//
}
this_post = < this_pre.nodes U { name }, this_pre.edges >
nodes.addElement (name);
19 September 2002
How do we know this still satisfies the rep invariant?
CS 201J Fall 2002
11
Implementing
addEdge
class Edge {
String node1, node 2;
}
class Graph {
Vector nodes; // A Vector of String objects
Vector edges; // A Vector of Edge object
…}
public void addEdge (String fnode, String tnode)
// REQUIRES: fnode and tnode are names of nodes in this.
// MODIFIES: this
// EFFECTS: Adds an edge from fnode to tnode to this:
//
this_post = < this_pre.nodes,
//
this_pre.edges U { {fnode, tnode} } >
}
edges.addElement (new Edge (fnode, tnode));
How do we know this still satisfies the rep invariant?
Would
edges.addElement (new Edge (tnode, fnode));
be correct?
19 September 2002
CS 201J Fall 2002
12
Implementing
getNeighbors
class Edge {
String node1, node 2;
}
class Graph {
Vector nodes; // A Vector of String objects
Vector edges; // A Vector of Edge object
…}
public StringSet getNeighbors (String node)
// REQUIRES: node is a node in this
// EFFECTS: Returns the StringSet consisting of all nodes in this
// that are directly connected to node:
//
\result = { n | {node, n} is in this.edges
}
StringSet res = new StringSet ();
Enumeration edgeenum = edges.elements ();
while (edgeenum.hasMoreElements ()) {
Edge e = (Edge) edgeenum.nextElement ();
if (e.node1.equals (node)) { res.insert (e.node2); }
else if (e.node2.equals (node)) { res.insert (e.node1); }
19 September 2002
CS 201J Fall 2002
13
B
D
Representation Ideas
• Set of Nodes, Set of Edges
A
C
e.g., Nodes = { A, B, C, D }
Edges = { <A, B>, <A, C>, <A, D>, <B, C> }
• Set of Nodes and Neighbors
e.g., Graph = { <A, {B, C, D}>, <B, {A, C}>,
<C, {A, B}>, <D, {A}> }
Each entry is pair of node name, and names of
nodes it is connected to.
19 September 2002
CS 201J Fall 2002
14
Implementation 2
class NodeNeighbors {
// OVERVIEW: Record type for representing an edge.
String node;
StringSet neighbors; // A Set of String objects
NodeNeighbors (String n) { node = n; neighbors = new StringSet (); }
}
class Graph {
// OVERVIEW: A Graph is a mutable type that represents an …
Vector nodes; // A Vector of NodeNeighbors objects
…
}
19 September 2002
CS 201J Fall 2002
15
Rep Invariant
Function from rep to boolean
class NodeNeighbors {
String node;
StringSet neighbors; }
class Graph {
Vector nodes; // A Vector of NodeNeighbors objects
}
RI (c) = c.nodes != null
&& !c.nodes.containsNull
&& elements of c.nodes are NodeNeighbors objects
&& no duplicates in c.nodes
&& for each node in c.nodes, each node in
c.nodes[i].neighbors is a node in c.nodes
c.nodes[i].neighbors does not contain duplicates
19 September 2002
CS 201J Fall 2002
16
Abstraction
Function
class NodeNeighbors {
String node;
StringSet neighbors;
}
class Graph {
Vector nodes; // A Vector of NodeNeighbors objects
}
AF (c) = < Nodes, Edges > where
Nodes = { c.nodes[i].node | 0 <= i < c.nodes.size () }
The set of nodes is the elements of the c.nodes Vector
Edges = { { c.nodes[i].node, c.nodes[i].neighbors[e] }
| 0 <= i < c.nodes.size (),
0 <= e <= c.nodes[i].neighbors.size ()
}
19 September 2002
CS 201J Fall 2002
17
Implementing
Constructor
class NodeNeighbors {
String node;
Vector neighbors; // A Vector of String objects
}
class Graph {
Vector nodes; // A Vector of NodeNeighbors objects
}
public Graph ()
// EFFECTS: Initializes this to a graph with no nodes or
//
}
edges: < {}, {} >.
nodes = new Vector ();
19 September 2002
CS 201J Fall 2002
18
Implementing
addNode
class NodeNeighbors {
String node;
StringSet neighbors;
}
class Graph {
Vector nodes; // A Vector of NodeNeighbors objects
}
public void addNode (String name) {
// REQUIRES: name is not the name of a node in this
// MODIFIES: this
// EFFECTS: adds a node named name to this:
//
}
this_post = < this_pre.nodes U { name }, this_pre.edges >
nodes.addElement (new NodeNeighbors (name));
How do we know this still satisfies the rep invariant?
19 September 2002
CS 201J Fall 2002
19
Implementing
addEdge
class NodeNeighbors {
String node;
StringSet neighbors;
}
class Graph {
Vector nodes; // A Vector of NodeNeighbors objects
}
public void addEdge (String fnode, String tnode)
// REQUIRES: fnode and tnode are names of nodes in this.
// MODIFIES: this
// EFFECTS: Adds an edge from fnode to tnode to this:
//
this_post = < this_pre.nodes,
//
this_pre.edges U { {fnode, tnode} } >
}
NodeNeighbors n1 = lookupNode (fnode);
NodeNeighbors n2 = lookupNode (tnode);
n1.neighbors.insert (tnode);
n2.neighbors.insert (fnode);
19 September 2002
We need
to implement
lookupNode
also.
How do we know this still satisfies the rep invariant?
CS 201J Fall 2002
20
Implementing
getNeighbors
class NodeNeighbors {
String node;
StringSet neighbors;
}
class Graph {
Vector nodes; // A Vector of NodeNeighbors objects
}
public StringSet getNeighbors (String node)
// REQUIRES: node is a node in this
// EFFECTS: Returns the StringSet consisting of all nodes in this
// that are directly connected to node:
//
\result = { n | {node, n} is in this.edges
}
NodeNeighbors n = lookupNode (node);
return n.neighbors;
Almost…but we have exposed our rep!
19 September 2002
CS 201J Fall 2002
21
Rep Exposure
What if client does this?
Graph g = new Graph ();
g.addNode (“A”);
g.addNode (“B”);
g.addEdge (“A”, “B”);
StringSet neighbors = g.getNeighbors (“A”);
neighbors.insert (“C”);
Does the rep invariant for g still hold?
19 September 2002
CS 201J Fall 2002
22
Rep Exposure
• If mutable components of the representation
are accessible to clients, the implementation
exposes the rep!
• Clients can mutate the representation
directly – without using data type operations
Why is this bad?
19 September 2002
CS 201J Fall 2002
23
Problems with Rep Exposure
• Client mutations could break the rep
invariant
• Client code may break if ADT
implementation changes
• No longer possible to reason about the
invariant being true by just checking the
ADT implementation
19 September 2002
CS 201J Fall 2002
24
Implementing
getNeighbors
class NodeNeighbors {
String node;
StringSet neighbors;
}
class Graph {
Vector nodes; // A Vector of NodeNeighbors objects
}
public StringSet getNeighbors (String node)
// REQUIRES: node is a node in this
// EFFECTS: Returns the StringSet consisting of all nodes in this
// that are directly connected to node:
//
\result = { n | {node, n} is in this.edges
NodeNeighbors n = lookupNode
(node);
return n.neighbors.copy ();
n.neighbors;
If we return a copy, the client doesn’t
}
have access to the actual neighbors object
in the representation.
19 September 2002
CS 201J Fall 2002
25
Which implementation is better?
• Depends what we care about
• Code complexity
– Normally the most important criteria
– Nodes/Edges: getNeighbors is harder
– NodeNeighbors: toString is harder, addEdge a little
harder
• Memory Use
– Nodes/Edges: 2 vectors, each edge requires 2 strings
– NodeNeighbors: 1 vector, number of nodes StringSets,
each edge requires 1 string
19 September 2002
CS 201J Fall 2002
26
Which implementation is better?
• Performance
– Both have poor performance: linear search
through all the nodes to find one
– NodeNeighbors getNeighbors does less work
– Other methods Nodes/Edges usually less
work
– If we expect clients to call getNeighbors a lot,
NodeNeighbors might be better
19 September 2002
CS 201J Fall 2002
27
Performance Comparison
> time java GraphTest
// Using Nodes/Edges impl
1.220u 0.020s 0:01.25 99.2%
> time java GraphTest
// Using NodeNeighbors impl
0.660u 0.040s 0:00.79 88.6%
Very rough comparison…but NodeNeighbors appears
to be twice as fast for this test case.
What is the test case doing?
19 September 2002
CS 201J Fall 2002
28
GraphTest.java
public class GraphTest {
static public void main (String args[]) {
Graph g = new Graph ();
int numnodes = 1000;
}
}
for (int i = 0; i < numnodes; i++) { g.addNode ("node" + i); }
for (int i = 0; i < numnodes - 1; i++)
{ g.addEdge ("node" + i, "node" + (i + 1)); }
for (int i = 0; i < numnodes - 2; i++)
{ g.addEdge ("node" + i, "node" + (i + 2)); }
for (int i = 0; i < numnodes; i++) {
StringSet neighbors = g.getNeighbors ("node" + i);
}
19 September 2002
CS 201J Fall 2002
29
Picking Implementations
Focus on complexity of implementation
Your time is more valuable
than the computer’s!
19 September 2002
CS 201J Fall 2002
30
PS4 - Design
• PS2: you used an ADT we provided
• PS3: you implemented an ADT
• PS4: you will design and implement ADTs
to solve a problem
– First part: just design (due Thursday)
– Second part: implementation (due Oct 3)
19 September 2002
CS 201J Fall 2002
31
Design
• Break a problem into components
– Abstract data types
• Each component should:
– Make sense on its own
– Be independent from other components
– Be small enough to understand and
implement
• This is hard. Designing well takes lots of
practice.
19 September 2002
CS 201J Fall 2002
32
Charge
• PS4: Design Document
– Due next Thursday
• No code to write yet – just think carefully
about your design
• Tuesday: design, modular dependency
diagrams (read Ch 13)
• Wednesday, 8pm: AC’s will hold recitation
on useful programming techniques
19 September 2002
CS 201J Fall 2002
33