Graphs, Continued ECE 297
Review: Map as a Graph Intersections Nodes Street Segments Edges unique id and unique name Street Segments Edges unique id only ids for intersections & street segments overlap remember which is which in your code Bloor & Yonge 1 215,000 street segments 24,500 streets 1 Bay & College 3 College & Spadina 4 2 3 4 DVP & unknown(20) 5 2 Bloor & Bay
Graph Type? Undirected or directed? One way streets directed A two way street makes 2 edges
Might also have “in_edge” list if you want/need it Graph Representation? Adjacency List or Adjacency Matrix? 1 2 3 4 Adjacency Matrix Adjacency List Node 0: out_edge {3} Node 1: out_edge {0, 3, 2} Node 2: out_edge {3, 4} … To Node 0 1 2 3 4 0 0 0 0 1 0 1 1 0 1 1 0 2 0 0 0 1 1 3 0 1 0 0 0 4 0 0 1 0 0 Might also have “in_edge” list if you want/need it From Node
Memory Complexity N intersections (N ~ 166,000 for Toronto) Adjacency matrix: N rows x N columns Memory required: O(N2) 26 GB (for 1 byte / entry)! Adjacency list: N lists Average: 4 ints / list O(N) 2.6 MB! 1 2 3 4 Adjacency Matrix To Node 0 1 2 3 4 0 0 0 0 1 0 1 1 0 1 1 0 2 0 0 0 1 1 3 0 1 0 0 0 4 0 0 1 0 0 Adjacency List Node 0: out_edge {3} Node 1: out_edge {0, 3, 2} Node 2: out_edge {3, 4} … From Node
libstreetsdatabase: More Than Connectivity Intersections (Nodes) id name lattitude, longitude StreetSegments (Edges) Street id What street are you part of? This has a name Speed limit One way (from node to node) Curvepoints shape of street segment
StreetSegment: Curve Points Not all StreetSegments (city blocks) are straight If not straight, have CurvePoints list of (lat,lon) points followed between intersections
libstreetdatabase: Streets & POI list of StreetSegments unique id unique name (e.g. “Yonge”) Points of Interest id location non-unique name (e.g. “Tim Horton’s) Natural Features ponds, parks, etc. Not used in milestone1
Graph Reference Chapter 22 of Introduction to Algorithms by Cormen et al
Milestone 1 List of functions to implement in m1.h Comments say what each should do Good practice: documentation is in your code! Header file: comments say how to use Not how it is built Detailed unit tests in ece297exercise These give the detailed specification “Test-driven development” Most test functionality Some also test speed too slow fail One runs valgrind to check memory OK
Milestone 1 Unit Tests main.cpp (made by unit tests) // Load anything you want here load_map (“toronto.bin”); int id = find_intersection_id_from_name (“Yonge & Bloor”); if (id != 22333) { <your code failed the test, record it> } // lots more tests // clean up here; all memory should be de-allocated close_map ();
load_map What should I do in load_map ()? 3 basic approaches // Load anything you want here load_map (string map_name); What should I do in load_map ()? 3 basic approaches load_map does nothing Implement all your higher-level api calls by calling lower level api (libstreetdatabaseapi) directly Stateless Often easier to code & debug But may be slow for some calls load_map builds everything you could ever want Never have to call lower-level api again You have full control make beautiful, fast data structures
load_map Blended approach load_map creates some new data structures // Load anything you want here load_map (string map_name); Blended approach load_map creates some new data structures Use them for api calls that need them for speed Call lower level api when it is fast enough All 3 approaches are reasonable Your choice which to use If you fail speed tests, change your approach load_map not automatically speed tested Spec: keep speed interactive Your TA will notice in the demo if it is very slow (> 1 s or so) Otherwise OK
Clean Up main.cpp (made by unit tests) // Load anything you want here load_map (“toronto.bin”); int id = find_intersection_id_from_name (“Yonge & Bloor”); if (id != 22333) { <your code failed the test, record it> } // lots more tests // clean up here; all memory should be de-allocated close_map (); ece297exercise runs valgrind on a load_map(), use, close_map() test Must free all memory or will lose marks
Automatic Memory Checking Memory checking: valgrind slowdown ~10x Testing: want fast & thorough Small test case: almost as thorough as big St. Helena
load_map How Do I Get Its Data? main.cpp (made by unit tests) // Load anything you want here load_map (“toronto.bin”); int id = find_intersection_id_from_name (“Yonge & Bloor”); if (id != 22333) { <your code failed the test, record it> } // lots more tests // clean up here; all memory should be de-allocated close_map (); ? Easy way global variable
But Aren’t Global Variables Bad? Yes, if you overuse them E.g. could pass argument but use global variable to avoid typing! But almost all programs have some Or something very similar Limit their number Group related data into classes (e.g. StreetGraph?) organizes data and avoids lots of namespace pollution StreetGraph find_intersection_id_from_name (“B & Y“); load_map() close_map()
Global Variable Pitfalls StreetGraph *streetGraph; // Pointer to your data! int find_intersection_id_from_name (string intersection_name) { streetGraph->answerMe (intersection_name); // What if streetGraph isn’t loaded yet? // Crash! delete streetGraph; // Thought I should clean up, but my teammates will be // surprised when they try to use streetGraph next! } Anyone can use / call functions of / delete our global object Would be nice if we could check it was set up first
Object-Oriented Alternative Design pattern: singleton Have one loaded copy of a certain object Many parts of the program need it StreetGraph find_intersection_id_from_name (“B & Y“); load_map() close_map() When function asks for access Return a pointer to the singleton object But can check that it is loaded first
StreetGraph as a Singleton class StreetGraph { private: static StreetGraph *theObjectPtr; // One copy per class public: StreetGraph (string mapName) { if (theObjectPtr != NULL) { cerr << “StreetGraph already loaded!” << endl; . . . // take some action; else { ... // Load up stuff theObjectPtr = this; } static StreetGraph *getObjectPtr ( ) { if (theObjectPtr == NULL) { cerr << “StreetGraph not loaded yet!” << endl; . . . // could end the program, or could load stuff up return (theObjectPtr); Can call without an object Replaces our global variable: like a controlled access global variable
Calling without an object use full name Singleton StreetGraph.h class StreetGraph { ... public: ~StreetGraph () { ... // Clean everything up theObjectPtr = NULL; // Says we’re not loaded } somefile.cpp #include “StreetGraph.h” void myFunc ( ) { StreetGraph *streetGraph = StreetGraph::getObjectPtr (); streetGraph->someQuery ( ... ); ... } Calling without an object use full name
Further Reading Common problems, and how to map them to software designs E.g. singleton pattern Inspired by patterns in architecture
Team Formation Dr. Tallman
ECE297 Tutorials, Jan 21 & Jan 23 Your team will meet your Communication Instructor (CI) and schedule a weekly 30-minute meeting beginning the following week. Wed, Jan 21, 1-3pm, students go to GB404 Fri, Jan 23, 9-11am, students go to GB412 Fri, Jan 23, 3-5pm, students go to GB412 If you do not have a full team formed by these dates, come to your scheduled tutorial, and Dr. Tallman will assist you.