From fcacae1d5f5846fc6c45967f221bb619a41bd47a Mon Sep 17 00:00:00 2001 From: David Gleich Date: Sat, 16 May 2009 14:29:24 -0700 Subject: [PATCH 1/7] First version of build script --- build/buildgaimc.m | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 build/buildgaimc.m diff --git a/build/buildgaimc.m b/build/buildgaimc.m new file mode 100644 index 0000000..4e1fc63 --- /dev/null +++ b/build/buildgaimc.m @@ -0,0 +1,84 @@ +%% build the gaimc distribution +function buildgaimc(varargin) +if ~isempty(strmatch('all',varargin)) + buildlist = {'test','pages','zip'}; +else + buildlist = varargin; +end +tobuild = @(type,buidlist) ~isempty(strmatch(type,buildlist)); +gaimcversion = '1.0'; +mypath = fileparts(mfilename('fullpath')); +oldpath = pwd; +matlablpath=path; +try + if tobuild('test',buildlist) + fprintf('Running tests...\n\n'); + cd(mypath) % move to where we think we are + cd ../ % in main gaimc directory + addpath(pwd); % add it to the path + cd test % run tests + try + test_main + catch ME + fprintf('*************\n') + fprintf('Tests failed!\n'); + fprintf('*************\n') + fprintf('\n'); + fprintf('Build halted.\n'); + fprintf('\n'); + fprintf('Test error:\n'); + rethrow(ME); + end + end + + if tobuild('pages',buildlist) + fprintf('Building demos...\n\n'); + cd(mypath) % move to where we think we are + cd ../ % in main gaimc directory + addpath(pwd); % add it to the path + cd demo + try + publish('demo'); + publish('airports'); + publish('performance_comparison'); + catch ME + fprintf('******************\n') + fprintf('Publishing failed!\n'); + fprintf('******************\n') + fprintf('\n'); + fprintf('Build halted.\n'); + fprintf('\n'); + fprintf('Test error:\n'); + rethrow(ME); + end + end + + if tobuild('zip',buildlist) + cd(mypath) % move to where we think we are + try + cd ../../ + [status,result] = system(... + sprintf('zip -r gaimc/build/gaimc-%s.zip gaimc/*.m gaimc/demo gaimc/test gaimc/graphs',... + gaimcversion)); + result + catch ME + fprintf('******************\n') + fprintf('Zipping failed!\n'); + fprintf('******************\n') + fprintf('\n'); + fprintf('Build halted.\n'); + fprintf('\n'); + fprintf('Error:\n'); + rethrow(ME); + end + end + +catch ME + cd(oldpath); % move to where we think we are + path(matlabpath); + rethrow(ME) +end + +cd(oldpath); +path(matlabpath); + From c83ad5f580470664eaf6d34ad9db3a77cce8e928 Mon Sep 17 00:00:00 2001 From: David Gleich Date: Sat, 16 May 2009 14:34:14 -0700 Subject: [PATCH 2/7] Added warning to performance_comparision demo --- demo/performance_comparison.m | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/demo/performance_comparison.m b/demo/performance_comparison.m index 63a8f52..8e042ee 100644 --- a/demo/performance_comparison.m +++ b/demo/performance_comparison.m @@ -6,6 +6,10 @@ % considerably slower. This example examines this lore and shows that % |gaimc| is typically within a factor of 2-4 of the mex code. +%% +% *This demo is unlikely to work on your own computer.* +% *They depend on having the MatlabBGL routines in one spot.* + %% Setup the environment % We need MatlabBGL on the path graphdir = '../graphs/'; @@ -237,7 +241,9 @@ %% Summarize the results % We are going to summarize the results in a bar plot based on the -% algorithm. Each algorithm is a single bar, where the performance of the +% algorithm. Each algorithm is a single barcopyfile(['..' filesep 'dfs.m'],'dfstest.m'); +graphs = {'all_shortest_paths_example', 'clr-24-1', 'cs-stanford', ... + 'minnesota','tapir'};, where the performance of the % mex code is 1. nresults=length(results); Ystd = zeros(nresults,1); From 6b07a7e851cd166fafd43d89d37be5912c098263 Mon Sep 17 00:00:00 2001 From: David Gleich Date: Sat, 16 May 2009 14:46:49 -0700 Subject: [PATCH 3/7] Fixed demos for publishing --- demo/airports.m | 9 ++------- demo/demo.m | 14 ++++++++------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/demo/airports.m b/demo/airports.m index 0f2aad4..af1970a 100644 --- a/demo/airports.m +++ b/demo/airports.m @@ -67,12 +67,10 @@ % That's the US, now we need to plot our data on top of it. [X,Y] = gplot(T,xy); % get the information to reproduce a gplot plotm(Y,X,'k.-','LineWidth',1.5); % plot the lines on the map -%% -% We need to clear the axes after the mapping toolbox -clf; + %% % Let's just look at the continential US too. -figure; ax = usamap('conus'); +clf; ax = usamap('conus'); states = shaperead('usastatelo', 'UseGeoCoords', true, 'Selector',... {@(name) ~any(strcmp(name,{'Alaska','Hawaii'})), 'Name'}); faceColors = makesymbolspec('Polygon',... @@ -85,9 +83,6 @@ % One interesting aspect of this map is that major airline hubs (Chicago, % New York, etc. are not well represented. One possible explaination is % that they have larger delays than other regional airports. -% -% Clear the figure again before proceeding. -clf; %% Honolulu to St. Johns? % Before, we saw that the lengthiest route was between St. John's and diff --git a/demo/demo.m b/demo/demo.m index 68fe5da..7778ae7 100644 --- a/demo/demo.m +++ b/demo/demo.m @@ -83,7 +83,7 @@ %% % Load the example matrix from the Boost Graph Library -load('graphs/dfs_example'); +load_gaimc_graph('dfs_example'); figure(1); graph_draw(A,xy,'labels',labels); %% @@ -99,7 +99,7 @@ %% % Let's look at breadth first search too, using a different example. -load('graphs/bfs_example'); +load_gaimc_graph('bfs_example'); figure(1); clf; graph_draw(A,xy,'labels',labels); %% @@ -161,7 +161,8 @@ %% % Now, we just call MST and look at the result. -T = mst_prim(A); +% T = mst_prim(A); +% This command means we can't run the demo, so it's commented out. %% % Oops, travel time isn't symmetric! Let's just pick the longest possible @@ -220,6 +221,7 @@ %% % We also have a largest_component function that makes it easy to just get % the largest connected component. +clf; [Acc,f] = largest_component(A); graph_draw(Acc,xy(f,:)) %% @@ -236,7 +238,7 @@ %% % Load a road network to use for statistical computations -load('graphs/minnesota'); +load_gaimc_graph('minnesota'); gplot(A,xy); %% @@ -280,7 +282,7 @@ %% % Load and convert the graph. -load ('graphs/all_shortest_paths_example'); +load_gaimc_graph('all_shortest_paths_example'); A = spfun(@(x) x-min(min(A))+1,A); % remove the negative edges As = convert_sparse(A); %% @@ -305,4 +307,4 @@ toc %% % And just to check, let's make sure the output is the same. -isequal(D,D2) \ No newline at end of file +isequal(D,D2) From b0fa7d689fa91f54418c81e80be6117adce85010 Mon Sep 17 00:00:00 2001 From: David Gleich Date: Sat, 16 May 2009 15:08:57 -0700 Subject: [PATCH 4/7] Updated to just a partial performance comparison unless I request the full one --- build/buildgaimc.m | 5 +- demo/performance_comparison_simple.m | 131 +++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 demo/performance_comparison_simple.m diff --git a/build/buildgaimc.m b/build/buildgaimc.m index 4e1fc63..e2819cb 100644 --- a/build/buildgaimc.m +++ b/build/buildgaimc.m @@ -40,7 +40,10 @@ function buildgaimc(varargin) try publish('demo'); publish('airports'); - publish('performance_comparison'); + publish('performance_comparison_simple'); + if tobuild('pages_fullperf') + publish('performance_comparison'); + end catch ME fprintf('******************\n') fprintf('Publishing failed!\n'); diff --git a/demo/performance_comparison_simple.m b/demo/performance_comparison_simple.m new file mode 100644 index 0000000..54c8905 --- /dev/null +++ b/demo/performance_comparison_simple.m @@ -0,0 +1,131 @@ +%% Compare performance of gaimc to matlab_bgl +% While the |gaimc| library implements its graph routines in Matlab +% "m"-code, the |matlab_bgl| library uses graph algorithms from the Boost +% graph library in C++ through a mex interface. Folklore has it that +% Matlab code with for loops like those required in the |gaimc| library is +% considerably slower. This example examines this lore and shows that +% |gaimc| is typically within a factor of 2-4 of the mex code for one +% function. The full performance_comparison suite evaluates the rest, but +% it takes a while to run, so I only run it when I'm interested in a +% complete picture and can afford to run it overnight. + +%% +% *This demo is unlikely to work on your own computer.* +% *They depend on having the MatlabBGL routines in one spot.* + +%% Setup the environment +% We need MatlabBGL on the path +graphdir = '../graphs/'; +matlabbgldir = '~/dev/matlab-bgl/4.0'; +try + addpath(matlabbgldir); % change this to your matlab_bgl path + ci=components(sparse(ones(5))); +catch + error('gaimc:performance_comparison','Matlab BGL is not working, halting...'); +end + +%% +% Check to make sure we are in the correct directory +cwd = pwd; dirtail = ['gaimc' filesep 'demo']; +if strcmp(cwd(end-length(dirtail)+1:end),dirtail) == 0 + error('%s should be executed from %s\n',mfilename,dirtail); +end + +%% +% initalize the results structure +results=[]; +mex_fast=0; mat_fast=0; mex_std=0; mat_std=0; + +%% Connected components +% To evaluate the performance of the connected components algorithm, we use +% sets of random graphs. +nrep=30; +szs=[1 10 100 5000 10000 50000]; +comp_results=[mex_fast mat_fast mex_std mat_std]; +for szi=1:length(szs) + fprintf('\n%20s size=%-5i ', 'scomponents', szs(szi)); + % Matlab needs 1 iteration to compile the function + if szi==2, mex_fast=0; mat_fast=0; mex_std=0; mat_std=0; end + for rep=1:nrep + fprintf('\b\b\b\b'); fprintf(' %3i', rep); + A=sprand(szs(szi),szs(szi),25/szs(szi)); + At=A'; [rp ci ai]=sparse_to_csr(A); As.rp=rp; As.ci=ci; As.ai=ai; + tic; cc1=components(A); mex_std=mex_std+toc; + tic; cc2=components(At,struct('istrans',1,'nocheck',1)); + mex_fast=mex_fast+toc; + tic; cc3=scomponents(A); mat_std=mat_std+toc; + tic; cc4=scomponents(As); mat_fast=mat_fast+toc; + cs1=accumarray(cc1,1,[max(cc1) 1]); + cs2=accumarray(cc2,1,[max(cc2) 1]); + cs3=accumarray(cc3,1,[max(cc3) 1]); + cs4=accumarray(cc4,1,[max(cc4) 1]); + if any(cs1 ~= cs2) || any(cs2 ~= cs3) || any(cs2 ~= cs4) + error('gaimc:scomponents','incorrect results from scomponents'); + end + end + comp_results(end+1,:) = [mex_fast mat_fast mex_std mat_std]; +end +comp_results=diff(comp_results); +results(end+1).name='scomponents'; +results(end).mex_fast = mex_fast; +results(end).mat_fast = mat_fast; +results(end).mex_std = mex_std; +results(end).mat_std = mat_std; + +%% Dijkstra's algorithm +% To evaluate the performance of Dijkstra's algorithm, we pick +graphs = {'clr-25-2', 'clr-24-1', 'cs-stanford', ... + 'minnesota', 'tapir'}; +nrep=30; ntests=100; mex_fast=0; mat_fast=0; mex_std=0; mat_std=0; +for rep=1:nrep + for gi=1:length(graphs) + load([graphdir graphs{gi} '.mat']); n=size(A,1); + At=A'; [rp ci ai]=sparse_to_csr(A); As.rp=rp; As.ci=ci; As.ai=ai; + for ti=1:ntests + fprintf([repmat('\b',1,66) '%20s rep=%3i graph=%-20s trial=%4i'], ... + 'dijkstra',rep,graphs{gi},ti); + v=ceil(n*rand(1)); + tic; d1=dijkstra_sp(A,v); mex_std=mex_std+toc; + tic; d2=dijkstra_sp(At,v,struct('istrans',1,'nocheck',1)); + mex_fast=mex_fast+toc; + tic; d3=dijkstra(A,v); mat_std=mat_std+toc; + tic; d4=dijkstra(As,v); mat_fast=mat_fast+toc; + if any(d1 ~= d2) || any(d2 ~= d3) || any(d3 ~= d4) + error('gaimc:dijkstra','incorrect results from dijkstra'); + end + end + end +end +fprintf('\n'); +results(end+1).name='dijkstra'; +results(end).mex_fast = mex_fast; +results(end).mat_fast = mat_fast; +results(end).mex_std = mex_std; +results(end).mat_std = mat_std; + + +%% Summarize the results +% We are going to summarize the results in a bar plot based on the +% algorithm. Each algorithm is a single bar where the performance of the +% mex code is 1. +nresults=length(results); +Ystd = zeros(nresults,1); +Yfast = zeros(nresults,1); +for i=1:nresults + Ystd(i)=results(i).mat_std/results(i).mex_std; + Yfast(i)=results(i).mat_fast/results(i).mex_fast; +end +bar(1:nresults,[Ystd Yfast]); set(gca,'XTickLabel',{results.name}); +legend('Standard','Fast','Location','Northwest'); + +%% +% From this, we see that the connected component codes are about half the +% speed of the Matlab BGL functions and Dijkstra's is about 1/4th the +% speed. This seems unideal, but the code is much more portable and +% flexible. +% +% The difference between the Standard and Fast results is that the fast +% results eliminate all data translation for both gaimc and MatlabBGL and +% are evaluating the actual algorithm implementation and not any of the +% data translation components. + From ec40e369e67f6eb4889ab174143c9d3ed5829e6c Mon Sep 17 00:00:00 2001 From: David Gleich Date: Sat, 16 May 2009 15:09:20 -0700 Subject: [PATCH 5/7] Fixed some typos and removed a useless plot of performance as a function of graph time --- demo/performance_comparison.m | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/demo/performance_comparison.m b/demo/performance_comparison.m index 8e042ee..f823fd5 100644 --- a/demo/performance_comparison.m +++ b/demo/performance_comparison.m @@ -107,9 +107,11 @@ %% % Plot the data for connected components -plot(szs, comp_results,'.-'); -legend('mex fast','mat fast','mex std','mat std','Location','Northwest'); -ylabel('time'); xlabel('graph size'); +% This plot isn't too meaningful, so I've omitted it. + +% plot(szs, comp_results,'.-'); +% legend('mex fast','mat fast','mex std','mat std','Location','Northwest'); +% ylabel('time'); xlabel('graph size'); %% Dijkstra's algorithm % To evaluate the performance of Dijkstra's algorithm, we pick @@ -241,7 +243,7 @@ %% Summarize the results % We are going to summarize the results in a bar plot based on the -% algorithm. Each algorithm is a single barcopyfile(['..' filesep 'dfs.m'],'dfstest.m'); +% algorithm. Each algorithm is a single bar graphs = {'all_shortest_paths_example', 'clr-24-1', 'cs-stanford', ... 'minnesota','tapir'};, where the performance of the % mex code is 1. From b84965f62bd5b19758607ff4e22289f342e8cb55 Mon Sep 17 00:00:00 2001 From: David Gleich Date: Sat, 16 May 2009 15:38:48 -0700 Subject: [PATCH 6/7] notes for matlab_central --- build/matlab_central_notes.txt | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 build/matlab_central_notes.txt diff --git a/build/matlab_central_notes.txt b/build/matlab_central_notes.txt new file mode 100644 index 0000000..bf4a4cb --- /dev/null +++ b/build/matlab_central_notes.txt @@ -0,0 +1,35 @@ +Picture: minnesota graph (demo_08.png) + +Title : gaimc : Graph Algorithms In Matlab Code +Summary (100 chars) : +Efficient pure-Matlab implementations of graph algorithms to complement MatlabBGL's mex functions. +Description (5000 chars) : + +While MatlabBGL uses the Boost Graph Library for efficient graph routines, +gaimc implements everything in pure Matlab code. While the routines are +slower, they aren't as slow as I initially thought. Since people often +have problems getting MatlabBGL to compile on new versions of Matlab +or on new architectures, this library is then a complement to MatlabBGL. + +See the published M-files for a few examples of the capabilities. + +Functions + depth first search (dfs) + breadth first search (bfs) + connected components (scomponents) + maximum weight bipartite matching (bipartite_matching) + Dijkstra's shortest paths (dijkstra) + Prim's minimum spanning tree (mst_prim) + clustering coefficients (clustercoeffs) + directed clustering coefficients (dirclustercoeffs) + core numbers (corenums) + +The project is maintained at github : http://github.com/dgleich/gaimc/tree/master + +Tags: + +graph, network, dijkstra, core numbers, prim, dfs, bfs, connected components, +strongly connected components, clustering coefficients, directed clustering coefficients, +directed network, directed graph, depth first search, breadth first search, +shortest paths, minimum spanning tree, mst + From a23ce527e42b102d9562bf07386a185f83a2fb41 Mon Sep 17 00:00:00 2001 From: David Gleich Date: Sat, 16 May 2009 15:40:28 -0700 Subject: [PATCH 7/7] Demo files for version 1.0 --- demo/html/airports.html | 321 +++++++++++ demo/html/airports.png | Bin 0 -> 4703 bytes demo/html/airports_01.png | Bin 0 -> 3172 bytes demo/html/airports_02.png | Bin 0 -> 15350 bytes demo/html/airports_03.png | Bin 0 -> 15056 bytes demo/html/airports_04.png | Bin 0 -> 16564 bytes demo/html/airports_05.png | Bin 0 -> 36030 bytes demo/html/demo.html | 744 +++++++++++++++++++++++++ demo/html/demo.png | Bin 0 -> 4210 bytes demo/html/demo_01.png | Bin 0 -> 2913 bytes demo/html/demo_02.png | Bin 0 -> 3830 bytes demo/html/demo_03.png | Bin 0 -> 2913 bytes demo/html/demo_04.png | Bin 0 -> 2934 bytes demo/html/demo_05.png | Bin 0 -> 7692 bytes demo/html/demo_06.png | Bin 0 -> 3847 bytes demo/html/demo_07.png | Bin 0 -> 3312 bytes demo/html/demo_08.png | Bin 0 -> 10811 bytes demo/html/demo_eq02910.png | Bin 0 -> 440 bytes demo/html/demo_eq74756.png | Bin 0 -> 459 bytes demo/html/demo_eq85525.png | Bin 0 -> 120 bytes demo/html/demo_eq91421.png | Bin 0 -> 440 bytes demo/html/performance_comparison_simple.html | 334 +++++++++++ demo/html/performance_comparison_simple.png | Bin 0 -> 1463 bytes demo/html/performance_comparison_simple_01.png | Bin 0 -> 3226 bytes 24 files changed, 1399 insertions(+) create mode 100644 demo/html/airports.html create mode 100644 demo/html/airports.png create mode 100644 demo/html/airports_01.png create mode 100644 demo/html/airports_02.png create mode 100644 demo/html/airports_03.png create mode 100644 demo/html/airports_04.png create mode 100644 demo/html/airports_05.png create mode 100644 demo/html/demo.html create mode 100644 demo/html/demo.png create mode 100644 demo/html/demo_01.png create mode 100644 demo/html/demo_02.png create mode 100644 demo/html/demo_03.png create mode 100644 demo/html/demo_04.png create mode 100644 demo/html/demo_05.png create mode 100644 demo/html/demo_06.png create mode 100644 demo/html/demo_07.png create mode 100644 demo/html/demo_08.png create mode 100644 demo/html/demo_eq02910.png create mode 100644 demo/html/demo_eq74756.png create mode 100644 demo/html/demo_eq85525.png create mode 100644 demo/html/demo_eq91421.png create mode 100644 demo/html/performance_comparison_simple.html create mode 100644 demo/html/performance_comparison_simple.png create mode 100644 demo/html/performance_comparison_simple_01.png diff --git a/demo/html/airports.html b/demo/html/airports.html new file mode 100644 index 0000000..c8b66e2 --- /dev/null +++ b/demo/html/airports.html @@ -0,0 +1,321 @@ + + + + + + + + The US airport network + + + + +
+

The US airport network

+ +

THe North American airport network is an interesting graph to examine. The source for this data was a file on Brendan Frey's + affinity propagation website. A(i,j) is the negative travel time between two airports. Although the data didn't include + the airport locations, I used the Yahoo! Geocoding API to generate a latitude and longitude for each airport. +

+ +

Contents

+ +

The data

load_gaimc_graph('airports');
+

Plot a histogram for all route time estimates

[si, ti, rt] = find(A);
+hist(-rt,100);  % times are stored as negative values
+

Find the lengthiest route

[val,ind] = max(-rt)
+{labels{si(ind)} labels{ti(ind)}}
+
+val =
+
+        2855
+
+
+ind =
+
+       63478
+
+
+ans = 
+
+    'Honolulu, HI'    'St. Johns, NL'
+
+

Some of the routes include stop overs, so it's probable that is what we find in this case.

+

Graph analysis: connected?

+

One of the first questions about any graph should be if it's connected or not.

max(scomponents(A))
+
+ans =
+
+     1
+
+

There is only one connected component, so the graph is connected.

+

Distance instead of time

+

Let's see how the edges correlate distance with estimated travel time.

[ai aj te] = find(A);
+de = distance(xy(ai,:), xy(aj,:));
+plot(de,-te,'.');
+xlabel('distance (arclength)'); ylabel('time (?)');
+

Wow! It's all over the place, but there is a lower bound. Some of these routes can include stop

+

Minimum spanning tree

+

This section repeats and extends some analysis in the overall gaimc demo. First, let's recompute the minimum spanning tree + based on travel time. +

load_gaimc_graph('airports')
+A = -A;          % we store the negative travel time
+A = max(A,A');   % travel time isn't symmetric
+T = mst_prim(A);
+clf;
+gplot(T,xy);
+
+
+% These next lines plot a map of the US with states colored.
+ax = worldmap('USA');
+load coast
+geoshow(ax, lat, long,...
+    'DisplayType', 'polygon', 'FaceColor', [.45 .60 .30])
+states = shaperead('usastatelo', 'UseGeoCoords', true);
+faceColors = makesymbolspec('Polygon',...
+    {'INDEX', [1 numel(states)], 'FaceColor', polcmap(numel(states))});
+    geoshow(ax, states, 'DisplayType', 'polygon', 'SymbolSpec', faceColors)
+set(gcf,'Position', [   52   234   929   702]);
+

That's the US, now we need to plot our data on top of it.

[X,Y] = gplot(T,xy); % get the information to reproduce a gplot
+plotm(Y,X,'k.-','LineWidth',1.5); % plot the lines on the map
+

Let's just look at the continential US too.

clf; ax = usamap('conus');
+states = shaperead('usastatelo', 'UseGeoCoords', true, 'Selector',...
+    {@(name) ~any(strcmp(name,{'Alaska','Hawaii'})), 'Name'});
+faceColors = makesymbolspec('Polygon',...
+    {'INDEX', [1 numel(states)], 'FaceColor', polcmap(numel(states))});
+geoshow(ax, states, 'DisplayType', 'polygon', 'SymbolSpec', faceColors)
+framem off; gridm off; mlabel off; plabel off
+set(gcf,'Position', [   52   234   929   702]);
+plotm(Y,X,'k.-','LineWidth',1.5); % plot the lines on the map
+

One interesting aspect of this map is that major airline hubs (Chicago, New York, etc. are not well represented. One possible + explaination is that they have larger delays than other regional airports. +

+

Honolulu to St. Johns?

+

Before, we saw that the lengthiest route was between St. John's and Honolulu. But, does the network have a better path to + follow between these cities? Let's check using Dijkstra's shortest path algorithm! +

% Reload the network to restore it.
+load_gaimc_graph('airports')
+A = -A;
+
+% find the longest route again
+[si, ti, rt] = find(A);
+[val,ind] = max(rt); % we've already negated above, so no need to redo it
+start = si(ind);
+dest = ti(ind);
+[d pred] = dijkstra(A,start); % compute the distance to everywhere from St. Johns
+d(dest)
+
+ans =
+
+   735
+
+

That value is considerably shorter than the direct time. How do we find this awesome route?

path =[]; u = dest; while (u ~= start) path=[u path]; u=pred(u); end
+fprintf('%s',labels{start});
+for i=path; fprintf(' --> %s', labels{i}); end, fprintf('\n');
+
Honolulu, HI --> Chicago, IL --> Montreal, QC --> St. Johns, NL
+

All pairs shortest paths

+

At this point, the right thing to do would be to recompute each edge in the network using an all-pairs shortest path algorithm, + and then look at how distance correlates with time in that network. However, I haven't had time to implement that algorithm + yet. +

+

Conclusion

+

Hopefully, you will agree that network algorithms are a powerful way to look at the relationships between airports!

+ +
+ + + \ No newline at end of file diff --git a/demo/html/airports.png b/demo/html/airports.png new file mode 100644 index 0000000000000000000000000000000000000000..19847a638c728c1b81b08130b813b0265d5149c6 GIT binary patch literal 4703 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 TN;twxvJKza(aoXC&NulV)c~uq literal 0 HcmV?d00001 diff --git a/demo/html/airports_02.png b/demo/html/airports_02.png new file mode 100644 index 0000000000000000000000000000000000000000..6b650ab8297f40fcb2486f234f86503c2fe9a9ba GIT binary patch literal 15350 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 literal 0 HcmV?d00001 diff --git a/demo/html/airports_03.png b/demo/html/airports_03.png new file mode 100644 index 0000000000000000000000000000000000000000..6d38c92e70f11b284987cefb0870bed815eaf27e GIT binary patch literal 15056 zcmeHu_qnjtI4A;y(S>TAQqhDfFrvYJmn2NW!2>X$tY7d7aj9AOaq+!*BTCOJDE3A~j91iu78@No8uZ_29Tyu<9}9Qu7i!YFs?su? z^tyJ|^lBEKc0Mld4j!EJU}sD2)?V$27cb~vfaRoheKU`s4{L&WE48w;b4`!8| z%jtiH5}U-iV3@mtJDn^TNAEt2xNtj)@QPsX4fALAAA5Q4@&1B(-+4Yg>_WqRvEJ1s=lJjrbkr|5c>>*kmgzDn6`8 z)!9x=eA*3MXqzXohETnD@wv3c?tm2eCU4MyFJF8nA|QJ4qU6PYO|1WG{x8Gfw?UEF z%g<<$`8D{%uuN|?eGC9ye<%i~O^5@v1@+?ZH+&eFm)ay@PQ-{EQMGM5-`e;U@)vjS zlyXU`rF+4a-Te_@KQfmntp`R43lz%L9_0 zwD!6rCga_|T$R3%-PcE-@D6y01vUauY=Gz(n7Whv!(Ct}N~CzvYKKGRggoP0|TyS z@9;9BsL&};hdV5&+}~@qt!6>oJlKR`IzKR5SGQGmYqHz)y1x2LHT<4L4@D*ZEtqs? z#9PW#2iw(FkUs%azG#^a_gELVj287c&s^2K*17(IT_&?S?MieRA&cJMuR^yJUTs`d zvU2mYFhV)|v``%`kQE5-7>G)V=5?mi+dOWbLo0ZLfq|*em+ae8G}*4Z2U_TNYFcS; zZ+}|nhD@L3NHSK&*sK z0iQUCorUXBouriYDeWbCY{O`9D#<3LJ8x zlW%Rbk{!WE^<1uYQSAwF!L6r-{V%6C3?Vd$P<=Qv-;dD|QIBX@W#Q4=dZThpA+sM< zeAcO$)&7|Dc>IDD*gUd=Hoi$~|At16d7Z3&j7x#oUY%FUZu#g{S>3byO{wae9N&t( zNB2MnR$>4EG1RmT4e>boK!2iMOUAnA*Ac z_FTGT*npz9~-?QJ#pHMgnht zhO{6E=5rmJC8?uN;^A*;za{@~jb8+FGY)2qy|(I{Y;!L2Ql*IuS{#EMbaLIL5Pp#4 z&i^Adt0o8G+_c93y;SCI)TXW$|G&+M_-zeo#S(5pKTc$06M1N?Sse!ta+jU`a^+wQ z#%mG(_UrXX>t9sjC;RX=OXG<}yU?Lf=B^cyjn$R2iBC&!7dj$5%$hGvt(hC$Pkrlz zLLErC>)hTOjBnKpN?<<%t(&+NYU1UMi_|gYH64$i99N%;>6TnfjBdmKg7*OvQ5M}o z|0+0gUB4IvsIh+h>`5^F3DcOGsdh@t4c7-@hJj5me7_;rx4|jxwF5N!Y)!rcU9=l$ z)MMG8;ocmH@RFgMRz7QGK-+hsZCtneyTNz%F#03p5K-DYPv2}xELk&XRdh({ZamTP zF{kN)L7VVI%(vp#krZ2tDj=o?lU-VgL*9(up(dWUXay>`k0aji9hYJxwn(OsQLr+( z4}@7%YF%GnGn9E0fgv2FwXHGzx1hn7Y;9Kw3F%;vPLcDkDJ2Uj`2I}~`c#HTje!hT z56K-gpn(sYeBC7em~!YzFi2&wB^hHY@fQlP@@H>2{hAetnlNb=*6D#boPw^Ct-`U5 z1O2iWL2ycW5WWXhN-ns8n?5H23}Qm{i|b^j6J>Bsa9Sy$iEyIj#%$R4hjo zt9g!41{AAvn0017WO3C&aw%+*58)%-n)sWGpYcL!s?ZFy1LquY$& zED!5mnVntk+jGTQcccqNS51?;Bs;ujd<2)j21>ta z*uUvG(=aUCcxyK2TTp{oq3WDcF=$;p86EQ2I89i;L*}o#54MBXvmA%JJG-b`z8MYUAop z<{-xCUcNK^W|Zh6`!ITt-7ieGvyV+MXLfhO8vR^FmSvyXBndbVe-P_V_~X!eTvI=)m{+XWg2la<`H~CCr&fuAehpc{*#{B zt*=!}v}%4jPsDXmJmgovMk~t4Dag_GJUid}k9dHk0MpxmqC0JC`^=`tix?Da1A0!n zn>8|80F6R|9@)O1;EvaObd|3|dU-#cg{Zb0z50OhH=)C|7lo}59Yw}Oif*~g70wx< z;TS6ZOwLkhn61a&r!9+y zl}km1s$m;J-wf;I3P5=TkA!1j2855zdVNc0=V{+5XZ|Ex@V>42O8B5R413Lq{v>j3 z%7mdHPIC5&bV;_)ENn2Ys-pEmp5q|Me5G{Q!pyburQ+m_3(0%pB~VOaL0zCyRf)wr zcg{K%ShUQKc0om*Z)Z{$SMGxPe!ovKXW=5!_I7>uW3fadRU5kZHlUc2L*0mL!>lBr-`cLH*YpQ#+$y4*Ugj$b zQL+0JINriW4?O?3WMz!`ocC6>G(Cu1diorB8@qi5C3_`gHrRlu6eGt}v+;`2&cN6qZc65(hJLZx1xee~xbE(`0mQHUaHg>tilKFSFTA%#5*qC( z*{y}gL*E7N#=LeSKQvyer! z-x>OYs4n4}6^}!CmIw}OPU2BWMJFN47>%~KgPg5<0dEL7W&$jQ-wx*Uygk#_^WTU`Wc!@w{tkc{JWb zD_`U=70Z%%Zx1!nlml9eg*zK0rH5xM8Xt+6I9_hzUnAOCY=i}S>Mu{xGDh0_(|aw7 z=%ISKjq9v3A}wV!`2N?kek{eSI{XxtX8*c6D6L;*6Lx$!&t`W)yIIWwU$Tth-N)(o zMmv(6JCL8k)9k4ozaucz{K~82)cod8N?s16EP1Y{nTn78ek1-Zj!gZ>cbzfTnI!Nq zY}HZdT!`2~eST>3$im4JX~VZmh@1a#-uBGNV4sX@%fQF5AKD(7jag6q{TND^;Pn&J zC0Lk*F*J{{t*gzJ-Yz@|@gd!^Q;WJ<+Fo2dgd^;Yj)vT9BtOeU`fth2KwW_ini&=; zlG4`(c!bk@kem_r~`@(48x!_mS*@o_5vuWn!CU82n3^JXyShg7^51qai?+ z;Sls7FPdp3PG6_)WJakx>>o_PvL70KkZ)+#sYMf`^I6<7aQ|-l(8--=Xo&5AjPK66 z!NFL(yu`LxtLUy%ZG1($Ieiv!z7t4@SRo7kIf@ra#kXl17UO-%^CWQbN|1xXK1pn+ z*(d1oz8g_I*8wyc+#a+L2wXl<1{4|(_k+*F8{Nx=XdUByez?xyMo@!B)NJOy#)T{D zpAQ1oqW`wHbjo1*`}@OE7V5s~sWf*TGE=*&Ic3EtaHw2#3^wK={U?`|kHrYD26 zhYNZUY7b9G^R65VQB#+xsZ77qM!REw9d6`q8arDf8#trrumRH3v`!5Z11t?U{e`tp z+{KCO6nlK#tI-_RcOO|SIZmk+@7_g0(AN5NxyC%%6?nsW&Yvzffqwk+s_l#+{#ECk zgSF?j&cPE%<#m`cids?jP6>h$>r!nAmR#Sn`LJbsV6}HW*ldKx)6Og*@cUp%zQt~+ z(e6yE5anq||x&h`qf0A_cRxMs2@Sxeei5x3uWuX{!9VcUlUSHPBoU#5~L!82Aoc z6_>3{Tj4~fY*{!J?k7oI)#(U&-j}-PKh3I)L<4f@E?HZk0@Tz`a(*ur!pe-Rz<7Z4 zo=C|SSMikjwRHPMoA!(tn}`b9`iH@59h;wRUCV=#tlTY%C$MzqU#~;rU^=4z^7r@@ zn{yq!7&DzoQN8+T@O;9IZ41uYofi~;*#3Mo`v+;!hAZM#U-Wy^@0jr(La~D*w+2YR zX2_fHjFW^WrMf2db2?g`&+;`3`ow671j%o5n{&uA#8$|5tac;*EW1|jc5kzZe;3ZS zXJwHO?`Zv8+PqDB8sM3ut*6||&iZjMYLpq*KWXAH^z7prLl0qbcg&P?aLSdLYB4YV$XHCF+uAUUZq zoO$Z-fpLXox*X{JYDNL8iSS`@-ZBil`ev>@o}JPaTcCAp1ksZ3;X?>UAa3cQyh@jf z|4`GsfSP}@A^##Lzd{WB1%z3L7Q(ESUs=R?YGMa@x`Z3Zi`ZKcB#;R#+I6?7{IV-) z(2|57nV1rt4l24%lhA`mo59ovW;C{fp6bJIu>X9~j52`OzLFCBny7#RRErgE3J!%p zTf*fnCL0IabD{M>^agocwFkF&aU)R-`wvvbB<^}Rhi@gAah93gHgrE|>Mfh|xkMx- zW%XtG%!)*#>|E<;3-@Ras}Y;abT`_cOT#pW8d!3~%-}=pE4%<)DXyhCFAMH}&vNuU89O;rau*BW>1t+L6@fdQsbSIHQ& zIXRZ0BT3;`f}1i*2mkz7l8WX2D27ud#>W)#=R^jZ`u%T7pA7DMFE zhtX~j$cFE5H}p8=lxo9fo2~hwRbBGXv>f*X?tSGsQGZdv0dQ@xhN2pW5rR=TR z1Iq*S`@!Q1?|rN4pEv2M#rL;wp+EzltH}uHYoeJSOUZd7fwpuLMoJb6I z%bR~68@zCLT$_GgIV);6Ei(39?!x!;`dyMI%yz~bil3`)7o{*4peR*`sTrequVl2P zBV=1LjB2#-969WbCtiAJMs<HX)Z=HnL1U6w%u-12b-QqcZa}+wO0WHt}rs^11p+axV>f-gL0(W>~ zcyGvp)`c6blD8bDKcmG8576%8k z=|+*W_bouV8CuGkhsp-s-`|b+M@40G1Y1S7u9hS>qR0{yG*J_qrR{JzPGPhTQ_91MJ`8R|gd?FWTO`5*lAZcBDx zo$&+;3Bl+noiw3XSizS~+@s!-En37ne`1<~Vrd+@H%y1n{P*dEKR#Ls9SMzjf-Et>i&3#y#+XIT zjj|44PM7zR z1%a@@mhTt{Z`_#}Q9+)(^37qj$P+^kJRDaWDqVj#EqY(iy^NyD zjL5ETp)@T>lmXy=!_70<Crn%R)IVv_lIMVv65Nb-RZs)X5` zh2Z8@Ixi}^df$HMPOLPzwYHi6F#KK_Aff%VF|@h<)+nfl;-Yl(k*z!)AUu*Zg^*Ky zEkl(6SyaMzz@9bH-5#c92dv+6wbOm7f^UWAcZM1P52;pFad#Z>NmAEnsvTH!?!iDb zK;Ho9@oZ-IM$-FlwD63ni9FGS!pWWDh)aokLY`D9R;a++EjlX+s12#PxY2jS}uf9OlD6Zm$XBW)x)cwPZLOQBlA)k5#Re{S<7A8{69yj?f5 z`KX^*9%SzRy$xIKdMp)SNgDu%H?T6Ow|=eAPkpV{gph6?MbU>)H46aal9#8Z>GkhJ#rf?vcJ95MQ7>)z^P$b@wi)u< z{$S^zYpF*TL=x^i0g{PDT6DtipMGzFZ4=93bwUaFGD)Gz+b2j`6y;qJo)It$%J0H> z;U44C=%x*C?V`Atb4l$_!*Ab`i31hm-4Ej2<~9{_G!d5}di5&$8q1 z#J<;073>HzuRd*`$Pi4MbMD3&883{7E}rCv=bwf3DN_P?^b$ zqce!`yA{?BZR9-lrRq{>LvY_1jD9`uTF45ea?+K&y{8c->8Dsp+}`H>%iezd?rs?+ zS!lUE!0X;Uk~o!=h5O;4NyVNq%73@VrSujPIi=s86X}H1t?zz!?x2$4$j!}@*m;@W z)cXL)6zTT)&v<8gsF`Y7MTS>K75}RY??f^mGlu8Y_V&$#wfH%(*HQaJVQcE}0s6z%FG=M`)vf9IB3ssFmAkJY$1|YaLIR6fJ{(?1-GPd zFNecekJVQMUtTv!h62?7NOw9qML*=`;HGj5W#TmZ`-+jt#@*=~l1ClMzmMyOV{c=u z=c8s#jq=4kD(urJ;fXWbmn2h)uE9bPO348?!KW<)UiX;sflB)7*+FYw1^vW+V}f{5 z_d5k%>QyZjKN2EdVwSf9)`It#cHaoOd(yHTQvT^Xcj!-Zh&m$8O)abumBKx{yV+_>gMZ=UzZUd(=wj=W4=i1VJ~wl1jFh)+>vUzLEu z&ya;pG|ONOJ}P2ukS=TtkJ(+zEjPU@6{^7fB*WE`P6hvfZaIn+ZFXXX%|U$aqoUn)kRG z10Cphq4zxW-b~~`@_c;Od%dirGZZu~yJAk1i^A1jV26{istFnZJlovVt*@#;zI=X9hVdvxs9?KFNW9~;HZ=t5MPr(KpQe4|tR3hW?Sy)oA80N+a z-*i-;6&ou1Gj(oeIa(a~uqlt*$ZCQH_F2=H3*FZCPLX42KU9br`z;2@Y*A2hW-fVy z<32H%`soDWMN3U%sQOEQFZeulM{x}@OUW7^kbl5c{dJkCBHkmF4bl}c}og;T38#GtQoeicy1Ib!;ak);_EjAfFJ+$rK&Ut!7ASP z2wMWv>CwvSrdA;e;#L0)STA>^ZqH7h+x@EN6w%;P@|^6E%Yz$Yk#9-CCMVAH>_F9xvKo8ZZkV?rRu*i?a%z zu6b}=Y74f)NSp83m8(TKzh`~V*KcB6pT_)l6fyL7aBj+v9R@$_?_U06Dohst%gzjm z!?pq`HemvxxFc`v6yHymI}UhGT{K;@S@(4Kh>!E_EcTAd`L>&zc0l|IF3e#6`i45` z(?~}?K}Lm2wNCR5zRi3`^#*etHvS6^Fr}_TO4|s~3T980AE@Ldk6U>kH%fp|a>fcD zHe(W%4#EA--qP6Is-=X-@y3IKR*(75+lsiSo-L%+u+^kT&gVN?h!2Jxp?wT`C}4?M zARp;?@ls-uk;_LbWSrg8_lK4194wm!+qx;?*DU%zaQij-X z>AQA}BVSiacA!Vvc1LDLvMKspR)2|V3+!|Ql?fYXS7i1~<*_Z=J%%rXXBP19g(yWRTxA48Xo(5`BCY9xykCRhpyv|gZ?V7)BK_azKY}4n& zK)=d)_D~Y_&EGOi9`)JoxW<-6+a4>Sq_`(;B~0Ro1Mjx0qQ{hgv+s;KWyA^py#)8_ z^yLRY$MmeLsy>M%xNF`I`e46 zp;?yVTn|v3mk+$A>_4_P3Hpg1j9*&1VhNucB$ZP1#Cq@;SeW}&5W31~HUH5K7gsykMk45P_n?0*121X)+PF{=k zofIdZ!W*0Kmp2b4I<;Ga&*u~XkmY`bc|E$=HPImug#-5@v6}jp=+5$`jhBqJU4eYQ zGG;c-PR>kZUHq~4%7%`MuM_#5hbx?2{18~2KO7tIlsv50AO>;%SHL`AxNt7=gsY!Lm!K?lk@vE_dfNf{l0e+viY7B5oWCK zBTMYuC4-FJ;;Q|R^oRx4f8vKLko|_vVGbqp&-N*laUWZtcHe*F?QA#J3bDwpRO zwt6at%dOu_ovPg_<l9#8eV>bM=R=6~A_U*Qv z@2M=EKN+8;b6{}*hkS+~syQR6zqe_0s#AZ;ndJw=*i;yNRDK86O(^ zgxOEmKOut-)qy(A2{D}Sl_YqGFNFJouV6=0Y&7XE}m|tFZWJ)MB0+CuDQ=vM~~4C?Tymky0uuQy<9y z1f9s=XrC=PsOo5F!~45tHS8d%UDz`|l_y0)HX(@}A3pu;64M7lt)CSnelL$hMTS7+ zA_8JV<-IELaUce$fj8Z^B%y%a$nGntAtn>Z>ekmhq~0=n+!Wp}W{~0Q4>-r|g4Zi@ zeaPo}w+fCC#A_I#ZXK%~xttEKlw7-JOsug2?lg1fj+qjbT^FUwxw|i$8lALzZxoWS zgvp%aTzdM&?cN)@*%|}VOX5@sKw({RrONlFkX6P41~3P(L1M`?Rr{(uLPDc~F1q96 zR~(C`xy0uCFg%%qnpcc<|0JOnKA*F%vF{5!Tt2xa)W?e+Y4c(3i?ac3bESQ+A9j-E zD-#{PWD&NEIqZ=R2brK+w6aT`AM=e>hU<}e+!XG#32PtqKA5jNcJ@(tAO#6gUPx&n z5#bb-;k&@peHjgpR!i#u60t8Wg%dF)F)AG>yj6k;%a#Q+>|ve^6e$)Iwi^BQ6_iGh z7kVlqkC8E(EnSd3fMV#B&%+MWfLEtyI~8q&)&y79VkN3~C=|Ib1_gY2!sWL9W}QE6 z)-5centp50x@#equ)lJ#66fhc?m?&~jTU0$UXUr1j)O6l*(Kwm{WY((bOs z9=w`t*4}?tO=@r6ivXv4qU2g7`6 z&ygm+Ky%f*)fls7DQ_|ORWsoNLy<%0RE3k3{&d9l2CT=E?aHb{=%>7OZh7;C^ZdfU zE{`^SJ#W$0cqISHX@mWo8T$7(TYj_afP|}PD}ikRUgvC|1%E?4B%-hZt*kFy^6=OC!>3{egB#I9VopkE@S2TQ~ z_+}SHC_|Cm4~(2W3RuF93^5L7DTTseKg(Jm6M?7EDwm=aIUPD{)gSf5`3I*Oi~y7j z&|wS-+Qx}9GK(6bBy?~}>sO2&+ZhbXSIhs&46r0_(xP54_>k%bL04gz4n7If)X^em zVUxs@e_Ygzw`^x?n^ZU_ zi~EkU2~P|z1rXDWV>f(nd$cS@E=mxiAA>% zrbDgG|6$(7so{KWk zk~YR8D+0+&LQ&vVj4BjF@=rLjg;RKfub4|@(PX`?v<3CLD*$!xy6p+~?2G*Rz z9X!7?w@dqo=Arru>L**fu4wRRhXADxV*8FBB_N?+?~k6R+8N6r#J=M7p<{jU(IrMt z#4#9T9CGQD%~!jW@J>k)^@70xP;b?mMwx_+1D z0BdSm0KOS+QTPP3Hc$bHr#_wW4zP<3{?_BWahkSxS-n94Qi-WT^7nK}q{95moa_`_^bt=EZv$H-7PH768$ z1W%V~qdVljg#+XfhMDJaye>wBY^oZ0vTgw@0j64E{$6ozrrtn~ib+P91C_lGX+u#o zoFjwQ(!mUh=Ewx&&$?tqEJU=UNEVgOs!~;tc}B-;3#%G5xsM}56+T=w*w3gTi^lWz z3kRv50Tt>*_Cj+O`8~cS@n+mhSUcEBeY@HYmYPKSbf}Te9GLv2EiGtM{txtrz zhxfuuC!E@C4kD>3a)l|X-IZ*LBftI1lsa#++MmrfKh0y7VmN18(bY@5hNpZM18GRw;U+G4A`Ly9g_3J&L{n9U02U#3xq%15dj?d!gQ!O zP}=IuneOF60c|9$2S&k(Pe&#@x#4HdBvl7MGeYP0u%^I+MrLo!M)qpHh^2!xjSBl$ zm_ga-mEnk(o@nlq6s;+9%Hyh{MigcVVL|h_S;J4{WX3lpFPG-FX>T&Sq`lckb}rch zjo#MT>-@5t*483<{{Xq;ST|9Z#iw=R5$Wgj_)7t;SuxHHoV*|@2S7_YUG3QkrvFW9 zo|}guBVs2pHWqhT&w zqExwyK59%ZVo6Ale~+@&YBUIL>u z`ePoxy=b!oZ^2h_gld1|C)r5Dd70~;f4(^t*!?SrsU&D??if_cHM7FN_3413JbCR5(cdtS+x9c|T268#e2(Tyvg zP_ZM{m)|L=}o zX{JIhFR!q@IM0iR7(do6jh^_1JP`oLvTvOdb{@Nk5`Ed$DI|x!pdyq+GpDzF@188? z@fAW>G)A-gpMdNFoKNVXbG*O(0O=^qqgdTP_u2AVZ|L*F zzQm=3A2Co}e5%t@7oa}0`i069F}aynV(}%^R>TSzr|Xcg&V8`dv80F1hp7<7MJ7>{@O5WMZPA+Mj?QkZHx8FmA)e-cNRTQ#NR9=B5l6cZ6;ejYh zlEN7=w)K^R=6ow>33|{s-fFbvcv=;|dYZ~PZ+jX^?E&vLwE)}Un*(W^kP4AE6;gPN~LqlgrdH&kv#Uy?|${;PVQ1qeqH1Ao)?@;vO>& zB^31{_c_cXBDU!#y?F`gpPFp!v|mmg4tOZtzYHhUC)hIg*0gyjWElSbmJ6dK;KhQ~ z*3g*0Bmj1CVzLOhPgGpAiZdMb54=!*r&PnbO8?yNGv4Z@p6-BW?u{%%qI;atNxmp< zk@BxJkzzj`*k~ERo!OAYu6aX1G#Xuz#WZu>tGirM?9VQx6hZt471;IfPfm?Cg|R0E z^PI)qa**I>RNxnHc*=5{kNO0ezR=7}KW>by=XZPavo~+Z>WS^YwW-aEihp_%6D5k$ zg5**%fDB(XW;v1m2_5siZa6}H`h4{+6xE~8q3A(lth5F(pisk3K*SlT@3WL{->=c{ zL9;uMF~t+sz*~GlV1Cw%F7MXZ4U<+vmOQ?SM3(lwdyRql;)U+#xc_VZuf^e+j<}Yz WFrH{T2U&vo0xYjCS0!Wq^?v}pHex*h literal 0 HcmV?d00001 diff --git a/demo/html/airports_04.png b/demo/html/airports_04.png new file mode 100644 index 0000000000000000000000000000000000000000..3828376a97491c94c0911875db0e663c3b90e189 GIT binary patch literal 16564 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 AtpET3 literal 0 HcmV?d00001 diff --git a/demo/html/airports_05.png b/demo/html/airports_05.png new file mode 100644 index 0000000000000000000000000000000000000000..6112d3fb29b6883cf2e82c13ba89c74ee4510c2d GIT binary patch literal 36030 zcmeEt^;c9;+crpdNv9wnHGrfbASj5mbPOfZFb=KKgLF$bNHffUIuhd`YSkWp`{;i^T$M*T@3Eak-mkP(iDn3>8Hn#g8#*|#UEJQPrrysz z?QOlC-+{Oco!#wm#HB^FZ9j-S7885IB`zf=CM)+?On`UE20QxUztINo9K8K(J?(L{ zl?}C&RfM?=o$R@^ZM~iR-g&xu33I&y*#TO6^ha@UxNu&*P&Ry@yVq{{1LXK&({C~V z)i{cPzk(f+sYDaqo!>;}3U14{+ zHPWNV{KkXEh4x`&@b$;L3!iQ0?EBa?U@z8{?9zXW$6g_WT^_jq8eLN%tN->7Cj^Gu zNARzKV}X}I_Mb)*Asgd=8g3M_fd4e6S-`;mG-{=m5dUe|xc(oZ|J$Sgt-MZ`w0 zVMXP1jsz5!;|0tMgE>>tDXm1FY{lB`%!Y+lbTl#!G zWa>YBp6$Lhr77uuo~{*;PJjEoOCpKrzXVv;Amfs+4IqsfN`HpTu9Um7 zyE;^{Vrg@ow2^R^ajVZxKQ>_NUzmW@n7S49#NNr9(b8@v$iaLe`dyQLhPvbLDPYGc zLGlRLHb7rXYQnc`ks*m_2O)$vr-@aR_aWJEeE-=dQZ_&PrMn<3fhwGFUc~#jPFoqm zI#TGTJF4ior&t1>YU-k=cdYK8YFHZZA{sva4t|XtY-D;anmUpY%l&iX&$Xr!&LaW> z%YWTVoq7{fn*5iVUQ|n?@8$QFk+fJEim7*bpVyKVO6ed-y5D_mQZ}vF!7j-+ei|-0 zL!bo`)I8N?$^zfzofvksx%zngpKIR6RbhO_2AtsO9F^Nnza$8t18W?+q?0ySYFfmD z3A`_J-XwL>DeI1L`pGjD6So`{1^CUp#H#*U3hvIYR4b`#V_{Fpw+LO_SW$oW)ee%< zURDX`liq6L;e<5OS?9%z4mSZ-R{Elm1nu9XA;L2(9t5xQVGhB5>hoBOi(~V4>oA)> zP}3Sc4r%T3Xb!u9^`%=P>s+hHu9C6$Lll=p3-j>$=I<}xNt_@%#_xqjswLuMAM`&Z z5&YDgtPI9w^AnZ5t2uyPvuwVAGng`CAFGI(x(eU68?XN%MzK(Vmm+LU?*cAiFc}8q~RW?ytKl@!72QN zy*Xc^B%C;AEm_iZESZL^VR55B?VFd4u)j^*xk%Nw&R66at*j_g!&4!~8oj-E^GOOF zo;I;St@DqYvoPE=pP@rCX*@Q+@_-Llkn{GZ6?s8kz8??s=+g@0whfO&P z4o-DyK19?1XWV$wAOSg+NA&`v%M@TI2=Pr}y*?=ri)kA?k>Q*@j1v#1i{p3NyFVs4 zO9*JFXTwfKi=YQE>jqS^G}~Zc)nXU{YdRPr3nj#|@?d8MA%63UW!G%zDx9`XYcc)t zpV=o@;XW-Z-()$tiud6)s~5uIL2NCG9nSY~y6P@}5!a=_=ZzTmsAUggJT@5aWvlPs zuTTYzm#?6iJ8~3C%X$(Wokhm5T!&lb*V{2WRwc4;6@Ao^b0bnYf4{Hn9t950b20T7c66ZACsBqD1V1HD+tEyKQ9xyp&*%KWV90N#AGlgSuuX|-Ke1^&&$$8qXO*QaPCe4hHe(e`RMuHYyZAOs~$Kx#V`WIcs&gUABr z=9fC2!GPLbvh7k+IWCzu6se7qib>lGB}!~}oAM*UBwX~e2ZYOk4!$&gllGQ5V1sai zS3cHKe;19o61RT}X$S;vFO+iPyM4knpqROBE|m53en1-#Y$X83z#d#=ecd<~BQC#$ zfbiqkuE=oI?bi&Sr^P#IoBhRo=DLKicrLhyWdvW>aEdi=nOFI@`PCtZXq>$q4}FSN zvIE&f+*#Z{OXj0+Q|IP$4%@lFm-P!p(zzm9eE&CQ}HNy-t?GH_5kSTSnI*T-i1!_i`KpRAUR3E|VY|4$BABe^HTTjPOgqjBlUZ zP8LqnaMdblvbVYv`I-Ngmy?EYhzNF5D3*_5J;u`ybH7_EzKNdmFkgVE9q_02nq+0b{kLOdPWL--i+V$J8a zK2sT9VpHX1q-aI4yOe`Cb72$kfZyw3kq5a(rt&_TExiw;uspJ}5_~A@9>-9gX)PFf zpn2){jgn_ZYN=$gnQ@Y5h91*T=BQgt*3D}A*8oK(`u3=hZz>CANMQD{tw#Y@_X)n4Fj%H8i|lfUhYA|J3cOz|t*urzj#9rx=F2A;5!LDgQmI{~6l zI#MOKFLTh$CX-2aIs!{dXCfZMzo>9%Gs^~;Jw?qV?+BC*4|@VeH(J?)L8n=7X zEa6VJtv04_r82xILksSWKOq{Xuk%Md)C|5bo>B#(qW8X1TN9=EV0I=ADq*yWoy*Z9e(cejvaWT^M zB57eEpik18X02GgN2en!8%A_Ec5(E=TA)#r1f*Y8L_Oh6v4%q&INtu(b-V5?-9@{J zMjG-kjjZ`qPynK7Z?kDSyNMHV{_Zc)DinQX%w-kiQ6*F;t`-{{?andbVe;*CpkPS% zEK=o8QJ}g?W7y}shCTim&~j8*=E@V;P>XNjAz(MN6K+e3$<=q&GzF)?ztohjSpON{ zb_#Lt?@5(H+k9gR9_&SB4nhA4J#z5Q>dsxTyw_(bG|nFkKg-&fGSc;DHIr%7o(Gq` z$iU?Hl8mQmi2`k9L=0cG7>XdofUmvm37YdZ23y_aDVxC*3;-hLi1-Jvh6B#sAyJ*0 zYP5Wu*r|!R)U5#q4~(HNY?O{c;B!uJwyH6JhKj$|p}I4}E%1K5xlx`slW`y4r>5jc zi2-S7CMW*NrYHLr5Ia4j5aay9B#>r0dD-#ccg79hP^)~;BP7G^6X}>b@M(ISar(P> zn5WGN7TDqpz^BlLN3Q`G03zPaQhnTL45Ohrz{p#+TP+@0FphX_>P5xc)bm75y;<*r zMXD#1pKis>-^{N$;_bE5`8$>7)brRKn$>>q+y(R|Zr4he2MQMnM#`aXj{3@POM(2z zNsLT#XtypfS{1`FYb-Sxk6YDLJmjGOx;A^^Nam*m^U&AyI<7+*$n@wIs~@lP&oL2X2* zWnxVWz0huC>1>6FEg9h5<5lj1rOxF-tS!|}mg%tdsM5l*Vq2q&Dk}wcgzwn52GqPC zI;Ho{b3ArM_PAEK7i~}qAwHud$Rj!ly{ACTyH6D<=?iLW!K|Dn#6R{*b${kFV;5Y$ za;G^*4{~i>HIj0W6V@LLUpg=M)0QdCl(QH+v+S-tHvWlWfXp?hNUKDknui28Dgzdq zc{&T4uRq2sHF``)<2_jq&Aa5gu{}i*kZRfCyRb1(Y4fcvzfI%Ve;a57jNLaj zEbDihSGK3d5M)^)etjv02QnTHm7pAs|4u|ZJuMX;D-r&D1-ZUVVH1P3RELX8!M58Q zzQ0B3mTr`cbx4z3+pdrV$M>4J1UL@_+^pA^=HCDUABu=6Tw7+Z2t^lsP;+5@bVX?) zOB5KgN;zC4UET$(m_ff|lF2mYv_2(YwW_O-`AoncVjv3MFe5(K1=|W}sD{ z#H9EPO3FM3?=pBaPGKi2>&ut*uT}J^>noTr*G*JVZf+`dZwfMd)3D23*m;~AmsW~C ze%Vem+9y-xrx{$$g+(EVt*t_c@TnYGb4i;Bhd%pcQ_K>rk>tg{wM_z#UlSh~JHKsB zHIluSRP|!4?rw@&)=5;bb%M*y>dv#~34@{=U)LL|c=_2k9BtXr%A5~noS9#_pZddo zZb&azK4nzvrN3nRvx0RYumAE8N>6)9OIAWOr{#vi#@0|VyjF{P4VUdsKFxWr_VOER ze^GjG!Kpitf|<;=cC21I3=KQ=gASEeD1|oJ9lim$T$<*GSH54zWZzsic#t8p$vVGA zGXnoyGaHmmZd#|^88&wutTXQ3+0IvRRCn;1Vi|l=BJg;#x@Gw<-1f&zeQ@B0%c=X) z(u-e~JEVXbWAS^a!YFxJUJ0H6%{(@(!-lc=9muZmqG0L7s_w75>@>^dnfT7w_L03m zgw#M*_Icc}U`?Lq#QNYfSPe91P$cUE!yRwfKV+cL^mBI7dhtmp4qkNS$*1iUB^ixw zPwi`%`fyq)rWJVo?8f*FHO6SHwR4)AV0Hu_KO=EYOQ&TNt5r#TqeTgPhPCFn)LItjofjnc zgF==@>fC^+_q3B=Qx59bdUZ{M#|zT7@&nY`Po}a|*Qlxk=`rIN$PK{xVA-cO&8=;I z_5!jU5q*=Bc(iGf)j(WStS>p`e+* z5eas?r61KVYTbtPK|3oWQQgt=f~AKacTaUXai89n*fuDljA+tKvSxg)UREs;f?gz& zb|&|b0n+R$0j#2k<28nsa4=9Aq>+*$DTE%>8F#ogkAH z7?Yvc?CzIT`61a-VA&h>3%6l$S~6S|FEWo`67^r!0nKPDbK zT)JQi2Liv3=g2EHOZ`E6l9@>tXxP-D8#O7VESa z2~Cg^m5MQ#U5R=6f>R->%OjGfEXu;jIwr@^&0t+t9b6#d%#gA``Q9BME6~A#jR5>q2o8;YDsq+Jx?epAr~${Kev&tvs>L_ ztJgz-tA_=*WX9lcpCm|*5(D?~T0yZG%A~U|Wv+Jp_P$bRcsJhz&_3{RvGjuTuS~s zm>bshgOgE~o)Ec%?4+_Se^~mX_2WROuqSrL@BI-||8Py(S3U4Pnpu}SjI@`?RWcZ1 z^8R$t?Q4^ll#H)Z*6v#g*qj^Z^bGqrv}MG$t(V)M7J*8g=M=X4-;J{^jI5P(#Yx#KkDWSO#wOxMDnp)g~+xr z0SAUJU%J3wXDgV!1;jk#-|PEm&}lmBlKwFxZr*u*dXiHH8+w&z=%8?{gjlqrR2~#7 z=sujuqdT{0y$YjJjd6gzZf%M%Hp6_7U;pfk#eH?p{QF7bnh8Ycr+`Qz7010Dx23lr z7J6CS1v0O_ZxZ1o)X3fI!sV^;8@aFvZ$Xd_V7gc8wLXDyNpphT} zq`@ascdBe;Yt0cBuwFF=%+Bm|_>R%Z9;gQl9tBUh<*~!OS?-=*Jx8a$lNZ{Hv-nB{ zm$W39foVtVmBBf6UdpOUcVM89oBn{^g5M^=MYqeYZHI&pGS&?NSb*TpTymCf^1(hX z6e*`C@u{Dl@>mf0XYkKA|Ihj414CqjJ=6w+Y0mTShfDS0`h!_L;oJB4!I;n}lP-@R zjdDtVKIF|iV8b;fN|`k+lYI(%^`)TE5ybvm6E()%)`6TKmm(G)_B}=Op7+%cwG-j# z%=r4A8}?Q04?tcHHzp2|Q4uL9&~>X~>NOLxWP)GyPrPy1{_ZIy%C#z~uJ#(wJaZ{iEiT$DeV&qFE+`PD^vZ6TdVJ^g$voinB@w12vTI-HCOQgez1y3tIDAi%Uq_H;dKtg7N9K>ZlYZ@u55weSu6>cvoa05Rj+8l)1FTKYl^dF3)ie5&?!gk=TIwz3)YCicOka!cB?jg1 z=?nN#zxGYoflcbz3hd20heN+^dt$zTwW;~6d|RSi;b6)6nXoF^h@1)0ALwK0h)k|2jHi!u4{v{!n9r|ctGalVo@HK4`_x>z*%3RH^ox+=Cp6NY_rHfcn6JH~_uUgTTmwI7 z0$0uqyd5OnmXk?;!Wv_=*RBVBIlU}?%%{!Pms#n5p#Srxd(RC7t8*P_izdjT$Ee#Y z?Tx$SqT)Y(cI<>E9uKCO{{r56ONWiS7r6XSEx`4b9UUzh7Y>^ryCE|nszyk`P*Hyd zs&z0gSa)Ma7719}IVD)M*gQqz9rhNUAvFOP^X+S>k!`>6!A;Jnr_4hpok2^@fN#G)N~f;>>^Qd=sQ{o|Js1S^*}v4{z|<`ZowS1}onfmIJX+l=}HK2@7Fk3iO++*t(~7Q%=b|A{tN z2cJ>}L$*X!4AT<}%XsNH129iCAJ1Bdx!8ChW za+B%@h+-p09y8wyi!(#e87cG~s@=%zl{-@rf#-3 zjT(QZ3Tv~WJ-t#c?()HXQ9a#)EbFt0Cix2N%bMv158_fqKJ1s0IS$iZM%9tF#s)vI z30Ojqfgb}s@*>RpGxynhAGm!T!_Yp`&Ux)n7Nyc9Z}e%8rOegPYn+=j_>6IfvqO!e zLcb$kX#qE3fn%_%k*tA7^RodM`t-MQs{j5IFEvlmyLF}X0=eslms%OjC^Ty6nG{3e z8_`-Rm7E`2-}=eDheWVoJ!Oj2DC!8^_=%zEMI1v)(?~B<$^7i~=pK56KF*KqmpyA;w}Ii>qo&RJ3HQ zyExnY$WnO(RKZ`fihTXy)EHp2p8WVQXm|G%R%2J(I62kj*xIYS!`{o@lR1>g8MN#2 zRNhp)U(kEgVEjB+iXM)}>Xu}8$E&cuIz12N41fQlI^5p8`X#@4m{VWY@w2Vk16d1`_% zL5OLa04s3&E35qydu+0xcU%aU%z^e(a(bBp1&NywE2kTwbL2AeEinMnQu(5(DbMoZ zE3fE~JvnVZV=pS`x`xZz`D2bXi>hwxfb&|L+}0UwFpvKtgH;`uzWZ*r*`24uc^rx0 zBH;?@r@Zkq_m@!m6C7-+V~&1@vFX%6nA}IJ7|{(Wn>(EnMt&wg$|K0miNSdFO0Yj> z7#IU~^CN$G|G`H>;UzD?b^uAjKV=KMg+C2@=rbH&G78SQ3}PCm`hm_j(p?R^L%ew+ zg!!*c>KKINkwRlM*30K&Xo_s4$r9owWa)89%iAo?@AOv^LLH$2`{uL4iz)>JNdD4l;(LB?pH~6SjSH&3VH1a z`>k#r{G{y0os2o{Lfi5QuxH&b5B-bR_j~rQR??#I_IrHM+mfk;+hR++E(&N^6ho#`3S6Hpa04oXKGJARIhbj*R z#mh|wOUI=&-%8?4#{Fpw>~^C>y)p$e%e^d9%+>De3JO{E=z+kB}P@~}tO5f^g%>4wL8sgrWQj2-PIbDl|ZQ$033pQz=n-xzd7 zfkIb(SAL7Q?KWli$m+XFs&UaY*e4Ub4*vasxuL(~UCEN#@sCqHSTHY;DIZ3p>HDas zO~8GoCtTMzUd>Jsz4z*pxnv2@U%kQMQqcE-E-+t%6rN4D8`8$|75|$kUaNpqGazE4 z*Nvw>j8tE@^LER7h*!I_N_H6b+dkwBuWuipIn10vIj-!x~j z|1JGMcP0~*H{!j79JP8cJ8sQ@jb{JO3Uy_46LR1>J3Y)V2s+xR*L?+EcMf%?X&Z6t z!o%hyLHx2D_^F!I8Sz3)>!#P@2p3nj=6RXV3=qvB<2Kc$Ij9$t5L3RN|cO_y{OP+ zRN-fG{?zGz`&<4bR>t+?O{~mrjgnlY!LX4r64;t?lmm2)A~fZ})}W*clIh&%V~7%* z8u9_Anbr~13x8?x6}xaF1~F9l5KJNrH^R6N#1DNxB9; z>);v1jX1j6dBF#WxHuuPUijnB&hodR9i-Rz^#PF%RIDSuQLuV8u}9Xq#s{K^=SFgx zvgM1zoX0|sJ6bPWI%b$|JmSN!4hhurd4!2J}BLRG;R|f~W3YPMPGV~9yP;^R*VPU_H&9@n0qpG6S zRXX6ck95US*_-zw!4<``zEghK^vdFU2~dW7;r1X0rA`@tLG(qOnk^=U2uwh~hL>uQ z{PW3!#_WpGPQh>964EI~i@^?}>Q>OyH%Ozt9@y)e#zggg@9pMfE9+M9IZ~r@fXz}& zA2Nn%53RnpMWGvPD$k7j#*qsbH%v9@dEhtFIRo&AFEu9ycC#}xON)>xo5qT|QLSjW zEv)bNpcN?vn(-j*s@@ZR?O^zJ?o6wnuMlQf$=fJ}pRw>*x`4*F`p@yI2D?n|fSTJ; z&fl)ykX6XCsa*|*$$_q!8Z*`7zt7FoI9G3sDDNL|bi5wrXs@f@?%k7!-A=S{?VkB@ zm5OFF1rHO@Nud{lrI8LNhH&UJ*G#BLo#3Os-a|ph2XU)mn4iSSBha*J?Z%a3_pNj4O9WJV)Pw+8(%0&`7?;u#eT`o20`ZDTu$5y^alrgFb4WqX#Q@fe}jwbXXyfw)?U# zc8u8lg%O)$z~GvNqiLJw1)+tSwa0R2*rFPEQA-_C|NHixuR57K7mZKF5zjfsW9j4)5y=k&}PHp_|VdGY+Q3|b(ZL~7C9ze zwxp?~!lG}xuDsas)ixQ~7Z)~Y*Q&+U3%*>dx&3my+WXpSIIpbI5{YMCf3@QHC(=%6 zDvsTu$eyfid}M=#Cj9pR{h@2u0DdtFas>Z-nyz`3uBpx*Vyf52e&2It-x>byTh@J` zqI%6MFSJl<7^BBGub+gU@H1Zcs-BtSIsP$LB?IAfJoQxXfpgWI_0rb)wmjjdoY;Z+ zn+hsK%ll{RK9hH3BDU7aAbR#2S!x%SYI!Ysk7l_2Xa7`5f&SGvVtenvaCnrDwhP$eNFQR-O@w!zVNvjnaB4*cd#t zb^Al&jX&;VT@&(gpU#f;v=1Ub_8gN61|P#}AlFz0Vclk?JN}|m1*|MMcTuja9wXF0 zyWhKmkWN-QwJzRnqQk&hT;XZ8c22?~!!tt6`6vr(N58 z67+ed*h-OHY9WFQY^rpkKHAA_|9M%z@4s(MA(3)S`OMR?w`W54{g zHwgFCzTq8dDrC1l4b^O6ma-gNts<307M_Sbd-_kLPAasrJluKkPzUK&W_ zI4H9>6ZIiF(hY7e$RB!U1Loop!^m6>K8i^Jk5C*20Rhcj`&G~cA&kenapi$}mL(zZZkY@Zxk$XFyZvVGF0N26I22S@=eO zaD=`wDl))NdQ|LRG{0$ALrQW-*rNHTsSQi`;h)N9a}`nY_uox+(i%_ak%6>iLq~=8 z;7Jb=kLSkt;aw73k5kQqHh=8y(09$)*`70eBLzj--49LL_;|rSOKtWG^ZUt>>|Dx` z@&Q>H@cUtk>33%hrjYkb2!+CTQ?@9ns3Xf?Kw@}F`u1RvtN3n@cNUBnQP0;dd`*MF zBKC$LzaLR`>ZHwi-|Ksyf}%HO?ai(Cr6ngW-rt5mSo7a*q!`XQ_X zjHr&0L>$jThOlZn1@wV82EoND3!)-z?jsEFvxGoD?o&bEw3f6SL56A~8%K9e_u}Lg zkJc>|I}?9u+!OJ!m=}jexdx9)wn-GW=@k1soqp|zMVH?*JneI8u^EgYnbttlI5f+2 z_O9@{9AoOse!q72AstqzKG+&1QA_FP75(Wf@#^f|n`DU4;<}~B-%@3;G6^`^L$6s7rb>o{#%kM)jFa>@WD&+E~ zjq*OE1;S;5Mfu_o=r1X3j@Nm$stn>9X}zytan1nuM5X!p5z<~(2FQHW>xAs5zkho* z5OzgBLL^-a{TaDR4b;QKnzYZ-xkhbh;toyLRYGSl!@E{-TOjfYeArlfLY;5#VMW(- zK&rk;xoAlft}iLzl}o_zq?pM~{&gOJ6%kM=P7MlHP}rJ&Q`2Z$@pWcqe1pVgBzk`> zOgya6nIPl7K>^y=ava?xUCS?w_uT8x_#U$M^7G+UgkI)zv`up%wxVrASpG|($0B?E zN!Ylz#c6<(AmY)wFW-U_UiVIbkd;2v&~`{l=J>b^RIid2O){_HC{;();S)Cop)JLz zF#{vkJ$)B4$!QG*R%V4*3x1Jp?jLkKXH^Aqc4Wg}3y%i+;;?mj_>a{`v36}DtFn&k zmd+Q!3}Igdw1=HRyS$}E`-SY2!Yjl_N_?Dq2}lFDRsqta)9>n3X3O9P zS3+G6)IKkW2;UK~SWd6u+tz!_UXqGTDTK(m0B*9^%JzZ2SAY3G#?*=DU;HNblA3R$ zd}EY3%+K_aeAk^H(Sd9C`B`aGZ4@iwson$WgcFbi9#7FO9k8VttMbfo`rL==l*8Yf z`ev!@?h7EQsx}gM5Ip_R#2a-OMc{HIF13flw%3>xgGVWhuauo(0`o8q8wgRRidX`4 zyUB|o12gMWuWer3;cq_K%Eh@7>ya*`kl4G6w5LH2oNm8k;>3pC`xCnw^9ayEdAODP zdJw2ix#nfk{0CHs;e9jGRlp=u=!3@L*prQ$`bC-aYAW;HW0!C{2wH_{zbl}enVtU9 z$EB`YefQUNeF+iaKOJJB|MD_H>)D5OX9bz8SG?|dsr$^mQiSEh`JL4p2lZvFEqB6P zh{^+@$*D15j8KoLkxu^7#-S&26aItDYnii=*7D)7qZoOkG2Fr@C;k_7%_Iy=zr#)5tPJt8GvTMl(CK=Zp}^mVCo-eOsp0jz>|vl z}#7iQ*5|j+R9o4#NiDM*F zGGjFbQ^bWS{h86m=7y@fKW{WW+EoM0eW6nc@Dtn2YV(5~gw43WiMIR$DhxE8k09Bf z6+LBsKlR1N&G&L}C!f{y>0bk%Cx3oCc>uvTSv*o)r@?GjE~3j5n3&(EZ8l`$E?Z3f zc@G@+NeJaaNo7YVRNbC`z9OP8U=G0nV~mqVSbUMM#?MIL!$P;WWH4BB`i6t>V;iWb z-Jy9}U$Iod_42aJ=3l5(r?Ez!g>Ge8UP5G=q#e`@+1t8f_>3$NE3nqt@Il?fiOrY; zPq{Q1hl+PQSIJ*r3m}@du~o{X^*5b$$wn5}h_5u-dA{ai0cfx_3!w)|C2)*P78Dp~ zMT$*`wz*i8VROJbZo5Vr+f=#lY1Lxwu_kMxC_I=I0!=vZznwG>lH=QK02Q@hbCD+z z%D?|yDO_9+wkC(0ezVWRRF0NXHhvLGB&3Lz@N$|iXuB?@g87T|c9ZXd3is(YEBrkzB{-2N@Y;hp=SjMa2RkCESLc^8gB(mI1F9k z+tReaoEepxy6R?-n4fQDkv?hDgzpq)Ge{QpB>|d{$bUlLLA2p@uXIuQG2#Q`?Dgc% zM;K6A5elwI^!+;bJTH?)>S21;d>>C8>7lVnN8!P`?zdzNa{L5obWGl~tudUoI<_TA z3}C{>8M~_|ypbgE6sBFHz;bEt!eG^@2#%0rs!$dY=Yu84P|s6-N3n{j97fNmuex&O zdKo9QWcprseCFQ+J0&eC{7VkY@94$-VC>CN>{jKNj2G#mCMxdh+2=-A2^UzPeo-Yo zee$C!6F!0IV#j5|#hSa^Qs5uBoYZ{RKx}>Wl3dZ|PtNxz8D)pRQSxWIf%838ZW=uE zK#aV*TJ5qnxAp}#?hacVRx~e-8Y=!+FN8Su$=~3R{<7_*nBmj9!|Y`RQNzE?6Y;MV{7DJg^*N zmOXfeEe|VpIS7I5u4<0^9Zb|@Obr1oDrG0kcpud8aM4j78MDw$#w*Q4SAX9y^Za3? zlc!B@{R^uqe0-N;dO!)+zfLFX{m$mLRHsGb3x z@{hFE)Ng4z0>0!VXS?PybO<_Y7YAmh(A(1=Ct2)99S7NlM`U}~^hXN3;`=mC*^3Wp;0exY|-fySrEuu^>r6N%VYS!W2J{x3v+z-C!d~uo9#OCHB1b#B)ww2c-N;bXNy+;EC0ySPLhdzCKsM##61^PT+%hT&BRBmTb@DN;-i(y4b zlic@EmigykW0B{IX3G)O$V@QDKCC5Y^Z||o*4o!;wuB}DI@f_SX4UqM+OX9Q$v~s> z9{S@bD#7W?jTqi!1^RAK=hD*$Wv{YShQ!2}Dpt?=>Qd(*ZDI$r1R4Ew<1r3x#J#!o z1VgQ?8!enVzFuYy$xkRXQEBE^Nw-&}+k$}=#Y=sfCi4-F!lWhpL9@MSH)UpldKcwT|{?EXI0 zTuozH`Gypq{+TW|KSF(wCQoTW3;go5LCnzJud+u)OE0t=A+IatRu|>^kh%mNK}W1X z40lVGuWm`VJH^D4OUPc2$R$59cODq=K%QIfO~!@cT6SDh-fR|Rrs{Y4-W8ddzCD~P zVfMtif3MH|`>bZm933VGQ)%7#<%>fweL8aZys%~T2K=jLYHoZ+)cUe!(m(b1zH78u z&AdcMKJtfK912i573~ptWV*_AEj}b$5l4xpVfO4*b6d<_70e(tMYnlyq! zKUw#2;A!IN^LK_CH?ud9$?xj3g;L1JfMXQ0AR&i((y$Cq`Ej%AU0*uQa4nC0B;sE5 zoWdHNaSWTHaQjrP)weJ*JQ88Vyq3xN81L}@n4;EaLu4KxShYY0lXmRl3iog@wbhhF z|27XAhFotJfaCJKxbcwG|DC{?rPs0~3aa#bqZGvcO7%f{>6m#otM#BrODx+Ot2zc- zelN13QAzRip?|I1{z=-)J5nr|Dcy$7+<2!vV%vdImSURa7eCMS6Z@M!k89&|hL5_E z)Xm|HsKbR0rfpc_%7c$oeGNx4B8GS$EY!AX|Zs?88U)pze7VSR#WekXh^z zFh(X%S{hu2%N0>%gZo_vNTWJ43u*yYsqW4}7u zm@Of+FRQNt*0PrM0opHi^>!_#`JcIHQrSFJi#zU>F{;g?*pQCduam0tOV0s~H<65_ zQK06%XqwrRal_u{LR=3;phAdHf}AE}uHpUq{p$3k^gUWKp&DA}$cc}zqRz*kp~A!K zRU+;$pVKf36FB^lE;2rv{L?DOgKry1B^G$adi*?iY52JK%-{(@^G7d?V;ScJsn`#= z!R~;2UicF2WGuoH1rlEbLCB)Ek4+x;Mhw$}*Js|so(?O1xZ2`;@~ic#u!U5OtQYH? zKgC8!Is7`{L&Ol)M1v1$`hGj;@t3{XBLOORy=3v8tvapq#D<|0Q(K?(VrQJ&EBAVw z;;Y?L)c5Lhn|(G7K_ZSl zfkl?rwYTEHuxE&KE<{G>7t!phD$b9^w$RkNb-#F|-Z|Ia;KcPgcti3*1x`9af;@hx zAwUrEYC2`$-XY7o?-%+>K4UvA1e2~j<}l&~4gQ+%ww=wz})&a=2!71SCe9H6A>%$KofGwBTs8UAHp0GU@!e_xth}B=-&>21g zblHm!Jd4LSn9eqaoU;7q7ynZW(5{Y1wU{nsoCoKf{taX@^P-S`*X~0g(>C{`U*zpu zs)?e)>vOtS@#DaQENa=suif6Y3vn+A3fW{29-Z!`A?Z+!Sw;(SFKR|3$mW}C3t7j2 z%zu6IZBC^uuAeqhs-sGqP%t_Iya)!hsTnox9&DZIOnD%1#piy2pB=*&{Xaj1@U=1 zmIoSMN{{o}^J9hd`hX?(yXvrI(cM?*nPfuPeY@{r{Lv?M#u&sw&bo-1ntfE?on^je23;;Yf+0L zKVRGlg)YT?(37}bU{j8VhBcd>S6^Tupi!Uwu;FRiAe3{kmvu+5q^+KQcQFYK( zd@{tpY4&S1^u?P{YxS8lzbF3gX%6h{OgKzhgPgB)761)pPsMKp z=)F%yg5O*j+$I&w)}-}s{33#ZZzr?KpdE?k*41APeNgTu;QhUonbij+%={m!p3Z=x zu9d=sIsAp-Y)5Eg z9Vw7jjn(2TEhw|RDmDJse-sAF>UCEfYu0Q{Ri$&ZZA?~?q zrlk4wKKeD%5^Q?FEPz|S|Gs3jXtQ3OjZNc2l#$2rezn?l&sS#xnde^qe4tfqmY!oR zXR{0!rH!rea)l-FR|XixMnNole57e4DgcN`4zk36#2gW}gn|v~fZ)cTvexD3FOBz1 zN#D||mAvpeoIFgkUvh`KzgiU=aO1M=8iUBiIBR*MvcSZdP66}SFNi$fA4D1;3yy~* z!`n2Ymm=(C@*zayUM_W%#c4E!Ec>kNC2TvE|8U2z$-imF z_zj+#&$s=q9nu4{C6s98(f)lE84VhskMQCkH|STBgjB|uDm$a_J+|ftyp@;|tCnA& zG~%aj$S8~0_gwcgIuxzW<|+ zJ)*3HknCfRj0%wzWpj?b3gRvD2pLRMB}WpC#k$0o_%;|z|Sbwox;zOVP^_xSzm zaoqRob>H{vx~}`Wp3moHUA#eMyxkttA*1*+@ND6LP;6d`8^1Br**_J75j16ox_m?A zVup2m^{*;!H9GC4gDlWrsD;%0K9pVgdAo)zLL2GXz@Xfy(Nmh_y@Nx?Apou_9 zv6nu+jFx$GjcsPixY`{ETvL8y4r?r!@ek{oUo6(mPw!}>rT^W$HhHIAK5V=c?e~lF z!f?8+{3g5%LtWJR*SdY0|I4+Ss%UTAJb$$bBJJzAvOYS;(tg`)fPN>-h@uNLNdBEW z1HdjiAZa7B4Q>D03lGcJH|u6?4{c(9y!Gi?!^4hLy@KL z$2QC+;a@Z9rH?H+o;WnCT69hI$tAE@niIG@4&*yIziF+0)7( znrsD_Eiyb?c~PdDW?&6tQ8M}>;gv%b$5UT4F>A_7{9zgvmiBg^)dN;04<2G9%tW$& z+$y=)QC>r1D%MfemH2WK5XqHmD+xZK&ZY_g6xw>&Y>!uo9P-|{@- ztPtB%9b^9h^{z0|+errNK=9&Cz(21P{nH6s{Vd9?B^BA48A_>5*47g&^~5CqTg2$oHNh$7__!v(jJcfU z+*-v*yUR0*Im}r-*M2g4zbjTz$*8}7@8HSWl-$O>=HQz>6Fb)^{MkP8>M}KpuVz*4 zX&|NGseap{PkK{3mf%(Vb$m^;n@HX5T&+F?LkkT&j=%cEmcNI19eVnoJ2J$dSSB-M zR?JxU`3jg^Ba;L9<9Vjs8Gh1Byb(j`i2wO&8Tn*bB9;DL++%l=r$)wugb&A9=U$f3 zeXHq`?QVWQu|mCMe{)~68Uw`VHW$e!cJiXAO7y%7VWbM-ao~##3qDB?df$$}KlBpz zSE2-X{lnysmm%v3Y83zRpQ3^Lq)Qa8^Iiiod_BPa>&xm#Bk!bQ4o4Uj;y!^3Se$4= z>($iw#GAy-Y=hUi8qIf|t}F#hAWf2(h*M)lrtp_JUR1;WuS7js_S=C9oRPA^-}z05 z_io#J64=yiSdjBh^ZEemVN{KOiDxnKODr8FP}Y9+$k3Un?n2%rAM+`~)cViQ@B)yA zHaq{Qg|`+y(~{!zI=$f|S0YHgnu;nyuL0++^?7d~5^Mo!@Y;Ynym{dUyz3g% zd+9k{N`dMz;V%dsfxq*wMVZW*p<(;GLf6L)JsRCE;5Lz5E11v%V{=0bM8H3}T-09i ztn(`evZ*Wn`1L=pLY)FNo_-144z z&qs*z%0hF5-W1xF&>vRJvmK8a!pFl`6y9FG5OX=Py|C4jtgH2zRW$-b; zOnte_=OZI>$I?Ou9nGnK)_6tFJ0kLx#X{b2J~sa@_UiQY&(|h)^s{65R#apSyQS`Z zzOp6_QR4s=Qv_#^V@3Ry8f16Md^obAxM4ri1OIKt>NAmtw)6YD=VamlNtwLg(JCl` zKFUC3j-CJW!y5`_&#htHp{tVYemz{{tED5|u#_pRQ0eidwLVDk*4O-Q_fc2d5sqq> zEXc%llBBGgmfT|}atwM{`myUcSg z>P=-Q`e-IZ&8s~HJ@kAcK+0gG*uv<4Zl`VNuWLC#Nz{k-&btiRPToC`bG6Eqp!xT2 z_)vpLu$xdEkNd~qkqaVl9jg3znp>{CT{FVq7CjK+)}Rdb&3%QN-aPd^Tc4NJuT-o= zM{^`@$6r&@1SMx}i;<}u*FCwO;aP_8^|^WVia6~S%0{}SLZ>Uugvaxw%B)MvQQg0? zHu2V|S!u{%yk3Cbzo^(+c~%D!q<5qOUe~h1ID(Pf7UwrUZi>1hlnn+PH#-g6o0lnlJGm6 zkpnYkO~u+B6)JF&UKYvOdYcmb^Z5HhEZZ3V`bm?H zz27#-eyApOCxW z3@y7F*;W00jZeb~a2^$8QUQ8ezxByBYPHEHtT14Zs!4d;U99c4G)r4O@2C1KV#@49 zm(Oq$%-`GrGUQTA*Al_UOa>%EF!bV>b}ObTG1U1K-evRJ$5`DNN;wN#ib)u4@eK;6 zAIkPO{O%bv1xlgHDtvY<5xK)&!ivn`Z2 zr>hA%o++OFN;R73lq1Q(Fm!L5_Q)(rvfpJh>hXiyU#{IPEbETcg6fW9xHS8hLj=Zt z?3Ym{4O0U+4)qd$4D8aeUC!pHm|CwE1f*wG!-j0PRVUlWg0gJBrx^p;`&Pymbzu&4 zT}^h^U6q%oo;S^8F4M(O_~WZN)*#2$3vMNe!))}!Fo!nbHL%t<7^I=Q+z^AqUUY{l ziy2iPeC;H5n9g2$Z2?L=(Z;3j9v&y2B1lJOB>YZBf>pV1Jl0hJl5$T%p-C3(#+9n0 z(!cLi9zf0K6P2Y#uxMtwVtIQE?p%Xx#Z*KtU0sCCzoB~g2)wLw_m+z2 zR{(acJ27M{@Hres!9lA;qUevIF-GJ*=`GsS@Kw~DlmFB&Q|0?Nli5(JzIlKBeI#6f zZIherA^efRu*++kSykDPkogD3uLnzTH$<0bZeG|MUAP>13ZdTOa?{^4Q8DDIjqJ$R ze2bBlqDSBbm$0yW)@m=4o3YM~0E+Es9LefwE_Uj(Avhx95<@&I$({c|J_%F5+-!IF z<3K;`KcNvyw?ZourhhdnI$>0|xoeRPxpbx9y*jzw{|OVNy)Y4Fb6R=U`gh8z(4WmO zo1~#B5-1XHZdZ1esT`%4qn1fc4Ya z`!Q*1qd%}LRK+wnVgNZv3-(Lkg(Cq4I4GgNkG`i0&oe)_g}WBA)t8TMaFyU1T= z%g5;v+wNeT@4_1Jb&A}dFhrOLsY6Q_T{o`(k(jnXyt^};=0;hqXDK!JSB$IF7N;3C zu0l@_s;9Ln7;5+R`b187nl77&jVUv>Y?^)dL+H)o0A|lns?lGe`kLMLk;n^ody8rT z72;iqZm#huGD0XUG@!!pJbHNIOOuB42dNOJPhKMP^5Tv5(BCyJbMya)1dhc#oSiTp# zwTeO^8fOk!QykRkVi3=EuQ;ENbDfh&Zdo!2u4+wBe5)n9sr@Jv%2(MD_8yh29{upt zJl7oI)Mz&F+-7+CY`y=!tL?4bez54t^^utyz(*HFlo zBRnKb!Ar005`jPEMbsKYErW*tr=U5$C&JSG+nNwZCc4_secU24VgKO*c#<7dZOI2N_ea zZibkC;fp^|LvS)ZWiMP&*Ufn-c+=haheGK0@n5_5oSNk6o3yMI@v2>~6wNFlNV`me zWyyL9P0D?)K}SC;?6G3$=Kk(Yc0qYL%S10KgF40JLu4HP(H;XnajKIkgZ?nDQm#0gX^6! zY0TJi$XefMRKGy&%T5o$#hh1q557JEmJIZ-0g~Cz51xF@e)H#t!&!t(gbpiy=b4+X zIAl!FXw9xck}0QQL0IDsw7uBq655t%`-_Y^kJcf!@~>P;uUOkBV$06XvCPEd>TZSi zw^Dr#7Yzo!O196Skp{1_VAZ>ov&UG)O*?98aBipM(*aN%AV5PovQi=Lu=^3h(U8jJ zy2?+=Jv)+aW_SA3{#-)Fsnh%OgrcF=plLoau(@D0k}$+aVTmavX{7pba$o}95KS(5!XCz;b{ ztGsdzU!NMZcn^yQ1#ZrEr8R<99V0>#pA?y@?8p`}Qs)AIyGl1Ng;-i*^8M}E(+R{S&GAF*k&>-Ztp7GMlEKwzl?fn$@#@)m|=s>F^K@9`TVci6v6&juS$=G z?@vCfj{9l;5V0QcRR87M2b1J>#p(AnOoorYFnmETmC%}E2!KyPw0PQ523kpgN12?R z76ac3LTL112B+zA8Ma)}5KUh*KPqW>`9rS$>V`gPxh&0@tb(hGGFDtxy}4?`*Q~0J zfCK%%ITQhnrSQikWmO(<4iS{c2QiMT%CXaCX7J51Y&iOXg<{pJN_t}lfkk^3*B?qj zR~Ns;VhjuqkEo_Kxa*<#wvzd<5x~7LiWZSL26aR@*nRGIRTIbVO{vpY^*&VmwKy+R z)J#v;^A0p!>`w~szXRruAi6Go9bZ)|OL<^WSte-%SWp?@vgcH0ds*OvVIsG8Uddq>31O+!!5 zMha;&@QzxM70Z2Urs@y=Sf4%B3-diI`>SIugo*Vaw#xIKBDdTI^km4!7^l3Enr{{8 zobIy4TXwqwmj23@UiB*($nA=2B}RS@UAKNw>l8j25c*^POd;_+An%m1deE+?F>y&5 z7s7heePZqShwkkEtJTD*Kd7H~YqWp+h%x-BuPnd>8S2a4HVzqAY8kWk#YyBnGQugy zHJYEM=RexZEe#ppk$67vV08!IEQjRR{`-)opwcX;swwe$;k?G@KUc})fW$*joH!e;${7#?&Rgradl>lRlYB4(2OSqYHSVDZ6GJYn%EYrOArQwwKf9FiU ze!`fhy^L{M=%EFX(ne6~*O zx9Bk-09y_=uo>P}IQ4wv4sLonm-1)emWEANT1L%y8nP5!)xj^2aGSgF8M8%@fpXGt zF*{c+gjEEUVOwbE29Q$MHft`wjRu#zsDNgA7cvHtcLM5{=0A>I)!w{4sUFcAl+Kdy zgO^}zLtnfp;%noq)y8RWPvT*7RkXAu-Qr{Q;?%Q`gF;)|8#wZ=Dk|^vOcT! zdH8XFSsIvL=N=tg@|Im<1`FjQvWa>%@{iI%8MZ2m5+ZA^ zSKWVCC$v6QFeb7KFGm)KuYgDO1kn2GB^9a4mz3PK3te#-X#rmk8u$|J6~}D2%J8ml z220l6Ybi;QV*ciauZc9BhueB9w4TU@Vk@if{`K~CvPOvMO+%S;gFNS$AXj^DA}T)? zr1-gR=YZ}<|1uZPr|s>Er!NH-cW2_b=U{yK2jD^)((N^f_KbIeB?V^ZUYKuMom&+R zlc>HPchdTVV14R27rVb(*V>tfGiWxmcd+AtrjOt6{^&t8G4fl)N@90#Tn)EeMilM- zqxXCKGhC$bikpDYQ&BUEKrQaMoi%dvxnCiN8}~4rQ>D80y<-)u(Z8Xg^=5{@{)~~C znF|a=XD2v4^HMC7?*XfFbc8yfokAX_0&pOpj&IBzrRSD$rFajfd%yiV(`x9dt{mZ$L~#ilWL`2&Hl|%M9Kq7{U)U(caO`T9Uy5W^$mE! z^_+?yQK5IcQTA7fs4LGre$VscMrMo$61p2^$FFh76byR z%;0_OFqd^JD=t@t+3v%ttox>{^y=dR>1!7X6Yq;2{{|Zuq$eiBSva|^1-d9g%L!O^$0SKhKztHa zS(ndcH89OP=Y!5jt_m$3{VTTkLb*6v;tlFe(C#q=Mhoqvk)^ZcZHVZR;p(`$!7poL>`{9wy#Mcr>TKmUt+0P$rvT?LSbS7`O4 zfSPD>R`$4(r(Aa;*XYN+%bIRkZfm2|uiZcb`tg9vXL45pk81$P=@lfVHgU#3A`Vs5 zyq~BdlZ>Xu+NQHNGT8l8iAL+cE4=61WOq-SyD5jxo(57s6lK5FW zPJrQ0_2RkR9&H{cn1<_>YU}}6=kjZ{Lml~ns{hwWFb!Cdj*brga=lwA39ao8*CopP zk5$Xw<(j9;Fse?;c&J1k56G52`YKJ)K|(xkvDuiY;X@U5gf!TOE$)hH`S!eFVV;pl zkzq->?_;1m#|PqaYWXV_ebVyxt*oQpzG9OoxYvg^;OeA*Ic)nAT|!cB$EwHq(&d@m zUjrNQmW)xQ-Kc==>UlQ%zJw`ngeFRIZpYgx3`i<3->#72zh%)+qDU|55B2#gm^^wr z+Dx<(D4ZgNlMw$HvTPR*I$AsT;PMp@BL_{#p}b9U)>a?dsKA#?z|2RL1(dO^f0w&qLD`yuae zq>!@`)q$uj@`LgxZz+-Jee&K@mCCX=lyfsa) zR;$*!rtei-3m0tf6pwX3%I%xa3O7@cMKwIYFr7SzFIfuC9No0E&R3C%6UJYKV`vTkhER11HO&-`TIizh+sX=Y;y9jgbthKfH2 zilJ$lV+?_fx$T!<{+e!cCCFo%c8a@b|F(=ik^SXDlVdtxJN^7D3d+0d)g=m8v-f_z zGg*+pichT~5-K=pq~>zEV|$N#wKAJu0b2KP-$ucvUCROP!0fE9_`|WsVDa_%40Kvw zS=W~xP&!p`z&{EID9aqjl(7#CdOR^%(U5$PvUet;#KJ*xA#~Qfq4$X2-2MXPn{5Yq zB(n$oitE_DQK`!Hk@RPqZ|)>ZL~#3E<*cH!NTyqpf7m*UP3VkA$^^6yH}gahBf9_- z9$SZt$0sKixx;yd;8ULp*ha%b9EZ8B5YGmxv;-E@FpJ9><&Hi3()>ur09a`2vIRT%3hDgv?A`VJ=#;yHcEZR`Xn%|>TUI$*!8Zf!B z5~xQp>oFpnQtC`ea<>_>xHJjI@Qw0cPLqYxv#t4Vt@kkegEcVu8na4nh33K38-6A# zqMnth*?Q&w@hT%gX@Zq~f`^;ghq@jOQ+L}%J>o%RgMh9dr%WDK8QMRJL2quC7(%>L zK`kn!Bd*iE5UOq`+7Uf|EyGUoOKHX>Vq$h19@1@UuAX5+JK+(Od%4WL_;<16Z;9G= z<#6_)-4*@)Obq?Z`*^QzFbj8HL}1~v7)G_Ab}SPYgtv%UbX8T`XLiW5&=EU4Ox9#x){po8*L zF*lk5(#&o^r=R%>?6KZ=Fn6{nNAC;x(kw6>BQG0HEvjm;M^D>k(^&Uvv(ng>9XKHDuiIWtPA%nyG=eaMLU)69o-BypjZCz zAI6IJWKrkbwCQ~PW)s9e%eenSdE}u8yJxobC#n&quZbpHrf?0#lHADD@icQ^22YdF zGY>#fQsH%RdXl^azcIokV#c;ti6aqQ#O6nC$qtZZdH~UcM(Wb2K1^97#7rcI_(mSt zb9?b9vbQ@}{ZdrzoPBYWDX76hHzG%29 z$3DehlCKhrj@jQeq|0!{EnUa!U>F2Wa`ufQBEjkbaVZjW00w@#XA>k~W6$o9uv%Te zV_p!7p>bBEd`=4HEQ%HLX7e+NL#fpz1O)#TEKY6TP)^6<7QTG`hkevxj^3Q~W%+US zeN9;t?xABBT4Xq|u=55DnK{+1z$1=*=w^K%Al7l~pQ)A2jTTjvSi!PXvFt>31bcy0 zynaStUI5#>ntV(p)~9ba$y&xbdvibI@R|GRY)e)M4&paw4m+we#x&7f4Vr!t6Y)w5 zP)?;(B5l z=voe6U_|KK(-?B6PoNp1sE&(_2WN^MXQKZa;UX5`iqw`}iL*=&Q)!T{g0O zA}TxLRZDM{KGhYIP@qOFc6EAQ>!(Pr2QyULuuPaQzdu2*VRA+Chbeem$2Z6&_+sb9 zJ4IcDi=@;!;lLOiNo%hUhvJAE-e~ z93rdN>w$9qm4~A;JN8ba9LWxB4gZ=|JEP2^~|~S ze0ZeIB~s&XAy^~N+cRM`vzVy@rr@lSTQ40{l@HnZ!~{fY=4jVVDl)ZN|Mc=^0Z;jg zfAkxRwT1VUeDFOjsC-F@3+gM-OyO7v1ZboZP7a9&UNNNl5I$KbF`1 zQ9>UA%bPh%RMh^Z)RY4taF?dy;++&kTB1$+h}}J%i;AoLTTq&#Mg>hf*9W`UD03kSehQzNw}BgC*y`4&^=p zl11f(SZu^q$m=7u+408<3H0^y7BleN2rA`Q+tPWM+i?%b$aaCP;yu+AcG~$Qhjnqu zs6YdO!hnKl(9*iR#g{!y?)~&vNy0_gYAs((T_v&atHftrNy98Y10x*UAeC;A{ykbfi#!rR<_`-sD9ev+3Wicfh*zwnPZ zr@MQi`Pu$5C%#2<RLsvMfY3RqtPg2i zD8ij0w!D<4?QewdYNJ#BT(})q*JoDHuAs@tcoFzEV3t42=srQeUXsd;hVLd=yyTGI z3B&P%v>b_x?YuABY#UUvw+ce%OD4}A#Hmeoxa(^}mYXP@s|QSa)$aXn%&&0IhG&K! z^c^~9PD7h<{l8%^X{|*gxLu!jlLY|E%Yhuz&`-9AfeKj5wETZH^3pUo#M0#5CcA5@ zKl&napTz%EJfe*)(qpVEFyX#kalrN%T^WHEj0L5j)hTGa36 zqszoIAyQd;%^~3_2o3s?uhl+3ss|1T(`jBFoNlYi`o%Zgy=OR)x4(A2iO&33U!Jk4 zT&yt0uNYEl&0$FCrU;QD+M-vxzOMZDtOUuC0CbA9yoLXwnd^s-&;7R9)*!qA;axBF z?;Ee0RE@yXjO98#nP;J8MDxY#3K)BpNrurF`|+YG*NEhf@`RmgMeSWZzjZpNKQ9sF9wRwG(-s#0agW6pGx%C* zbNfhX->UG)(yeH-96IlB7%9bglN{=%VKEXcb^O`PtNC`794?TdNsF0%=c*E^`jf<3 zx$8cDN~-z^2Cp%fx4oc$B(Zq*3&atBedHb$%hF|v85X3C3%H1=3&}o@}&E$o*?}xQm43(mnbgFZ#Ju0!vIS|=UDa?eR&$VP&gH`F&bA`yQrbn^yEuzD9$j^?FxNp zR^!sQw3E8cXQFE*cdwOCnY&3YYBLAQeLabEs1BH{>wk0Q^ZKnr`wj1TkK*;48M6}G z_jBbb62R&#|7*vw@a~adve!@J+%DVjke9sLC_2LTfdN zsM`q#FX1Ys@joQQE1KQo4`D10lia&>(#LH8#qvmsQVal86%(&7zl5y4YLOP= zLL%}wR|>URW`(zvM=e}j2vf#jep>s{PH%NT>h^?~rJ)QaPX&PwIA1L{JSh5ni9>u`9wqQmF zX8vdT%M=VAo&8;VM6i%J}PdyxMs~u6)zcUUKdp0-Epq zl+Vc5tZ}b7yTCrxlYNxHQqjwjHLIYKn#IVEMfKHcpeL@ZV_M{7pzok9xewXwb!OkMX%wxO>#03oEA5L6 zqwOPah9OxL2>!zd#iecelJf(D1deWXTa{n-n9BW%*0Gk&v8IJTiL`>-gGNnxC*v=d z-L#)#yE6BPDi2htpKz;9&w6z|4z-%K82Dx`OLpTH`!#8&+Z6q&FiYRls@7aL-BWBp z)R^O36yat+?+FvUMdqXZb8v@0kZm#v+WiavW828M#!t~9fnQWSjpNLOgRo@#OIHD^ zSfGTl0U#8%cu(5nKvpxd7p@1k; zA!RdfG6YvFF%--^-x-)JA|DLu*1H;Tdg~nguk?Mc1fIeDR*9kF+p5T-5qe|9bR8zr zV5Ve&F#C+JPz#=4Ddy4FFGOY!7;vhc*N3ADH$L)vta@7o<<@hNs-Ge`HNH7?G>j#r z59`oJI=sI!0RAL4SUu{u8Ey0PC`@ao41jFs>2ZsFFPl|xOo+_J2F43L=E!(6JrgTk z1eer@C35{#(&qMc;Q=nU|1H+lOh7JKzt)B(-FN(tO^=d=MP(Cb!6_*`mSRJ2Ws;uMaTZ}Xy=4Rx_G95dKJ4<_PTHWAYKWBnP@l|7s7pc;v7S`<&ULO_Z&ZR~_MqxC z|3A_V`EB5v{rr-qnGUI@GTRJZ1GApmFb9y?UmJEoRjPzY84!|UlFRaEr>dF<&ZH6M z|CMm_lJW6%%*KeY$&nP6MqRE!+Q)#vRu4H@wbdDUXZoi^j_ji~JtV9~>Fz-l4OE}hhZuw^r6q;UTEmF&1Sx!mjpGlQychfG3Ivk>0 zy`8J3fOEuJ%lFwN=)XG5nYuD6|HjM&G}VjYFbw%UF1IbtL-^}+_Y08YCg} zx5^aAxPYFD%IcsPmnTTXZ_X^Co%it!Zh}L>u-DGF9CHBiRE$4Kw&4TE1)KH9jqe>7 zqiHi7XJp|tx7ht$u1*eLS;KMjV>WIMh|wYEwea*c2>?Emh4IMH=pUx%Mc_sH&3q5b z0z$b-S3-xVrY{{NO7IBs@RSV*8lkmhbXjn15yi65kbexpqnablZ+G&xD|bq0;oHHm z30t}5xtSY%tOLN^yM!@^?u@0SA(f-bOFeC&xpIa;K1UA>!Lc&y$81RUNC_!bQ9j+K zaJycj(LeZL@tH^3{8GH17TOcWMBE4F5s9uV~DVOv1X*hqnPe z^;RZIOX3bBYNg*y|BUiDq5HTAI``SgGBj3G(l&Cl_*!pBzl72_@_wEGP+Ab_o)AB_ zk8~5I{i0;{fLke??Dwk}IkP>+0hrxmWZsBA~ zz}KDjOtB@L!41-VFPX2za=Z18D@yau(w(T#oq6TzJ4QyF?Fv58na2*l8M@uy!eIwP z@}bj*fAp0tV{-}rroW`dHKWMh0OOb$6M(F=$J^F19<6lKScyu)$g+Wq@Zd;KVU%DT zy(%5oRy`~JPpSq`#+jfdqXz-+p83x=JR+fe@Awl;?}Q4Y!04<|jD7M*2YUYLeJXj8#W)OaU@yxX2C0eE^#EVXsY_p9y+*KJ)g8&D zT5wmDj}P5RRUY^u2Rj)r5J4w|XOTwUU7p_m_zW{ly%O`|<&K6Pf2)}fb#gNPZE0!K zh-B}vS0a~5iG;z}&Dr}GO`DrFPpk{?fyv;&VKyk+F#kW&hvd!rhT<=`6!1ug(40Y@R`m+EOsx zjS*dBOA)QqG()KPUHi@aq7%rj)U8jP`mO$V8NOV%urpiJt-YlGuUlhR`VYoU1zHKj zewqk!l*1`*-(Dy?c||lpC6S-&V%(Ke4L|PS=AS`>7*8voQz8e zJa>P;L<}MZr1V#lWLNh1g3CYY8)l>>*b97?fDG2Jy}Z@OJlUeEpjPhxz}Kc|{pLbI zIv=WxawyBAzxu-z%q>E8ch_ZPazw4t1x`+O8#ozl*PL4Fqbxt8Gz>PsZFD84wX(E{ zS@eKXbWuNflENi$DVDT&MmB4OQFs5P(ax#l&3-B;{3IUlJ)9%(c z1c_OEn%Nc`yXo?*@M-YT(k8y=1$k7wt3$wxnFY(cAM&&4!v7mfpdo^s)0$%-FiqJn zA>YnPt~%PTq}*0lyBjXWz=y>^ITGq$s*SK|H`aGxn=tu2U8fcHixk#84wd z@y&d`$g{kl4nO6use_qZpx6hCL5cf!V#WR!;oQFJk2`AfxDl>$QkfvR296$Ik55Ae zzt2C*YUfHg#_Yze0!`?&)uZEIxJCRCuYi4i?@9!hWkJ>)lFZ0D0nZg4ia$FKxipF6 zaS+qeE8xSJDKwf{39W0(#A8fH=H6AIlOr)N3y@!w-!jycQVfLIOK)Uwh}!joY*Ky5 zfu08BUx&{xahn(Gi_+3+-7>^?aDjev)x$rOAd!%$6iU*U4V1x?ckT9m!3FX6SFe?vfPd*E4X4k;``TQb@Fa{5+AQJ2l>mNp5f!mn8(nI{J_h`<}K9iF)iHc>@}TNF+D=5 za{a945*-%op(DZjTCb-wMqt5iU^rx~n(R|NU2A$>B2M9XO0nc@%3PLWCPVMV=hp=I zq7$k6wu<-9CK%H$FMPgIPjaLKV(SIWXKtG+Kv8zSM ze1@B?k`kRA+tla@)sQRm<;PLT=+F)=92JuTM~-;&7GuL^Q`dpw;6bTZ(s%A5INIW_ z`9QQmU-Z)hiRaSGMAAy%OmNwKqB^=+ZM?b`W-xbMDDFx8$P?*6se*O?!F1>Otu7h6 zg-^`CCx0uhfS3GtRb`5yN#1<<4JYf(3Lm&I-``P?G8xuz>=_g!VyTY>3A5{kBwhST zu0}tWRcm*8;L&s`C)`^y;^GSx8G}cKzm@p5uNVBK$qG~ zvo}ovzud5!Z0MPA6Kc6pZisK+`z`Py%ks=)UQ~+N?XA&4_CXhKJDxNY=faKLE1lS| zS0E_d6o&uH(+bna zupx<$@Ka)LM`2A~`yHziPi5@m{dS3$u;(LAfr_g7Lu>Viq7R)nY(TKIl@@=y-*E~Y z05CV2st7my_GR{9NX6S&Gxg^3;BQ>zbHneW$-jA~0T5TU6T z`0)cU)bJC~FmKG;Y3*5yK*y9m1<7;JbD`2bN3Esi1hzh__?Bl|o?!uzUg(zBk6BS> zG@I8tL8@(#G_AkI)cx-l@E8w-iVHypL`^6`clQbW<_Qd zGw{f6?zk|3TV}xTn^%|zA@iobXbMhtRP}c`Dg1R`TsX%0fm1}&R{iijRLE!by`kK- z$;AX_4%#NbTAcAp-fRX=)#)t3ZEfAqPCDb4Gco@A?5g9HAYDjNM^7gqbAfWGE* zMFCE-w1gQ6?4Iskz2Ck@JhX7wcJL8VyqLAfn}aSQo>^p9*Xm6_!pyR}n+;V_z;V@M z^8P=ozZNjAW8r(YxVrSku1SuNvu}$rB~h;J%vD)Qwa%kXkae^938}ZQ|JCmgEq(ie zLOhcpHE#6b1<8@hZSS&iq|v4ZtAI1K6ZtIHQyDH_MQ1!d7zfX}FJEUU(|dcRTk8j^ zVZdiIF*#6SeVqXdUOJZR`?A{%4KYe51l-bm?4tyDeK_ftfO6p8f#^sW02vtuBS%tO0GR=|*)Q zsheIrpqov}eA>LaK2DqKceG&XprU4Lh_rdGOzSnrmlLN9hNdc$oD4EvnhXtJa^rsZ zZB^YB@_Q2Sx1GPoc`nyB3VAE7R#XnD8ML@N5Hc1LC5>#|_5A)04df?*BgUTgnm6qE zgqj~L+9XqWKS0ZE*UzZ?Fk?R;Tlf8Riugx1e{rMc$$U$gkZ zyv-%J)WFKtbj$?D6CJPzs!_87U1rwEWtGy{*05z!*KGOk`7GE1fk89AF&oI-9yC;y0YkCvoPiF%^t_hP&Kyl1jtbw* zFKAqTUQS)4cIZW=5jWcXm=>0g;ZY?&am|AC0qU!xwtw5o;?ZT#3bMJ-2T@GGuNU@ z5WgcUs>~T?rib2;!n_r8Fa6#~1b&S@e{^~NE#=lDffWjyz(DmtU*^D?>zTDhk^!f_ zd(dx4NHa>)B2>gyD!_DlO{QSs{;Qv_cW?sf*cmIzXf5d(=8_Nh_vZzf1H=PKb7iV8^XGjr{rZj18BL#5V&RG7OiC) zzH>Q_Lbo!WQ!$xdKuUc7w%ZvQ!fKntpcd7c`W#>v`?-Unkf)sz+*$W|(U(5Dvg8?p z*u7Z%6yHks)eBT-?aEDOw-2}(Z>IWY5yv!z{*h{YNZG%uq#`l)k{_tf)cM|^N8@#^ z)9dllfnQXiefX}80X9=CWWO9T79h0F((@oMjH0tbU$c_(fj{1B`ZwI7s^J%{&uO#G zXHVS}1

rbDQIy42U($7w89e;j$HqxJFq&-Z6!<%2)4TZ0TO#yQ{T9cT=bF^|9GK z@bj|a2}YBqKc74B9S>eIaE$3qGBO(O-}_ znYmnk3m(zy1DR7>xwMuFA`SjJP0!D(aTRs4yuLiUSgFGdm$U56m9kA_4wj(tlZ6|w`MEs$|>87gA8v7QvLhI&0e)4T2`+|VZ z?(%tu>2x~GP5fkE5U@Bjje8^`Q?O||&x^N2bFK{DjmJ}cmKANk#lPVh8hqFTVH9)l^_8niyuBzfkuvbcqr^^`123Yen|ABwf4L@6!0)U!kO9rN`_|%>h2c(Cn`M2 z!B2jy`3;$ojjWoSOm2|olu1}Wi0-|fDF}~%jTfv?Blyv~46%>i*13nr2 z;Kvo<+5P?Q!X(&ZTGeN^egL)&fS zH42oA+YBj5&!)ZQB@k9JV1!=CH~jD#F7&mLA@V&+)dn-?Wz0B9sKp)n&B|`@uTZ%8 zzaAo}ep&kP59#+4I(&3I8rLt4yd`E1es8WOQ&CBa8M(&{IN2{MZ-rKK4O$oVuc+#` z{K78a;U;qu*}Tw+oY)L|B3>cYr*sghe?R*&yXVolwdl%$pMKvOmeZn%mx`g76gbwowwP*?hWrCSj85qDnNpUT|wyJgWG4aPR6tL%g}RlmKDz5CKyq3 z0K*zBWQz~RVJ^=qyZ4D}9Y^O(CL>b=YF5K61B7q6wLWCf?)rhu%)~ z;hPkAj}qBSe_Ynzp3~hckC(ThUAuV=!M}xiD2#bDLxQCz%kEz`PAt~tQe<+3^d!k@+eb+_BfR0&Jl+>JQ!_RHfUr(Zzz zPZLwRcDPKEE*Q-U-oW554ivXHj%qTp&hx^F(aN)JL&vhL1&xmO(J=gFf z=TcX_V8c6iaV-owmw<)K*)h_N1fASu9TPu zUBC9}b4Lk(pY~L01F-a(D!wOP~=l>b|84dRL>;7Fiy@NsDT~<8dUc!N|zc@c^0v2Abz`A~_@Pnf}pYtRLet*p{ zr^8B*f#D+1Kn8){9&QE)6C)7+#0~}qP-QqOI2s_sATfOYKi6*Mldu2&FJS-zPgg&e IbxsLQ06k5+HUIzs literal 0 HcmV?d00001 diff --git a/demo/html/demo.html b/demo/html/demo.html new file mode 100644 index 0000000..05f6e22 --- /dev/null +++ b/demo/html/demo.html @@ -0,0 +1,744 @@ + + + + + + + + Demo of gaimc - 'Graph Algorithms In Matlab Code' + + + + +

+ + + \ No newline at end of file diff --git a/demo/html/demo.png b/demo/html/demo.png new file mode 100644 index 0000000000000000000000000000000000000000..6f0c93aa916e716e517153bac2aff0294df1b620 GIT binary patch literal 4210 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 IM6N<$g0KcSnE(I) literal 0 HcmV?d00001 diff --git a/demo/html/demo_01.png b/demo/html/demo_01.png new file mode 100644 index 0000000000000000000000000000000000000000..2dbcfe26cf2230b7505de8139c847c587129ec6e GIT binary patch literal 2913 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 literal 0 HcmV?d00001 diff --git a/demo/html/demo_03.png b/demo/html/demo_03.png new file mode 100644 index 0000000000000000000000000000000000000000..9cad9bf4e1ed7f8b441c5e07df8652a6e8bdcdf2 GIT binary patch literal 2913 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 literal 0 HcmV?d00001 diff --git a/demo/html/demo_05.png b/demo/html/demo_05.png new file mode 100644 index 0000000000000000000000000000000000000000..e0c2922f942f6671bc4101348ac9ff2506b80564 GIT binary patch literal 7692 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 Jr`ao-_FrS)?FIk< literal 0 HcmV?d00001 diff --git a/demo/html/demo_08.png b/demo/html/demo_08.png new file mode 100644 index 0000000000000000000000000000000000000000..1612ed56551e783525b9979e3275a8fa6162c46c GIT binary patch literal 10811 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 XHxQrr_qW)C4bV{4eNnCaD(Zg#KuA-< literal 0 HcmV?d00001 diff --git a/demo/html/demo_eq02910.png b/demo/html/demo_eq02910.png new file mode 100644 index 0000000000000000000000000000000000000000..d1fbfd38fffe2c7ab15a3996a235219b05268e30 GIT binary patch literal 440 zcmV;p0Z0CcP) z6&x|lSOZi500C@CL_t(2k!@1Fs`5Y-oD(kofQ5}H`v_??U}0lr;Y*~@C$R7dgcNEM zu+q*#u#Dg%2x=jiYHOpNAgDyi7Vi0qbA4`{7lub0;P zlVRrhe7@Oi&gZk%`glC@7s4=HEEe1CmYMtg{{4Qp+U<5Wn_VuK0GJs703xz1i-_ZTCG-&;{XssL{TJ!0MPAr0Zb+nW6WeS%xv2>fNHhc>2!Wlsnq3i@jTD> zeIo);E|-N6g+jrse&5Utj^lWq=ejNc-}lq$w2|R2SjA#-zu%WirB7BV^?W{+Qt$Tx zP_Ng+Ff;-Ae7;ht0FYAldcAjck|abVr3A3s?OfM|QYr|7$K&zc*st16$?Nr+nWHE& i{9rIJAH)d0Oa2GQ{w7|;U~+E&0000 z6&x~EQBv*z00DnVL_t(2k!6vws`5Y(g=ZIo#nmE+D2PSG%EB&s1X>N<_3QtKDwD-|sl{csy*|W{fe$Y};0_;$7FJ0I=Kbf*_Dm0)P+#VtPKGj^o6g zS@YPIorY?@{iRs&-! z%3LlN%d+nGdoGugQW8P{fDp1;ts0HS;c(dR_w{;RnE;?^+I&8z08pt^D5ZvBc%D}( zm7;b4$Y!&JLP2E^(J+i&ub0VWeBY;(Mod1RPo+{q2qA>7>(PooKR?A{kq!ofbUGb| zVYys3O|x39DuNKQ-ELjirIbc@oX=-n*C&%nGMW6JX*Qd!Rx5%xo6YHT`hLGKolg7x z{^#?FKURrz-syCf%jF+GPR5x(4V?3Cw>z87egP{JMS76bHKPCk002ovPDHLkV1k|w B$cO*{ literal 0 HcmV?d00001 diff --git a/demo/html/demo_eq85525.png b/demo/html/demo_eq85525.png new file mode 100644 index 0000000000000000000000000000000000000000..896d5f68994c87a07c4350bca671d6d38c47ca97 GIT binary patch literal 120 zcmeAS@N?(olHy`uVBq!ia0vp^%s|Y_!2~4vg%5rOQtTz3zOL*ySp`I8j6ws0-T{Sl zJY5_^IA$h4dG^fAz~IO4@A}QHtw;ZZ!TCSGzrU~k{%+;!)yhXC9=>8|JR>!$A$0P2 PpgsmqS3j3^P6 z6&x|lSOZi500C@CL_t(2k!@1Fs`5Y-oD(kofQ5}H`v_??U}0lr;Y*~@C$R7dgcNEM zu+q*#u#Dg%2x=jiYHOpNAgDyi7Vi0qbA4`{7lub0;P zlVRrhe7@Oi&gZk%`glC@7s4=HEEe1CmYMtg{{4Qp+U<5Wn_VuK0GJs703xz1i-_ZTCG-&;{XssL{TJ!0MPAr0Zb+nW6WeS%xv2>fNHhc>2!Wlsnq3i@jTD> zeIo);E|-N6g+jrse&5Utj^lWq=ejNc-}lq$w2|R2SjA#-zu%WirB7BV^?W{+Qt$Tx zP_Ng+Ff;-Ae7;ht0FYAldcAjck|abVr3A3s?OfM|QYr|7$K&zc*st16$?Nr+nWHE& i{9rIJAH)d0Oa2GQ{w7|;U~+E&0000 + + + + + + Compare performance of gaimc to matlab_bgl + + + + +
+

Compare performance of gaimc to matlab_bgl

+ +

While the gaimc library implements its graph routines in Matlab "m"-code, the matlab_bgl library uses graph algorithms from the Boost graph library in C++ through a mex interface. Folklore has it that Matlab code + with for loops like those required in the gaimc library is considerably slower. This example examines this lore and shows that gaimc is typically within a factor of 2-4 of the mex code for one function. The full performance_comparison suite evaluates the + rest, but it takes a while to run, so I only run it when I'm interested in a complete picture and can afford to run it overnight. +

+
+

Contents

+
+

This demo is unlikely to work on your own computer. They depend on having the MatlabBGL routines in one spot.

+

Setup the environment

+

We need MatlabBGL on the path

graphdir = '../graphs/';
+matlabbgldir = '~/dev/matlab-bgl/4.0';
+try
+    addpath(matlabbgldir); % change this to your matlab_bgl path
+    ci=components(sparse(ones(5)));
+catch
+    error('gaimc:performance_comparison','Matlab BGL is not working, halting...');
+end
+
Warning: Duplicate directory name: /home/dgleich/dev/matlab-bgl/4.0.
+

Check to make sure we are in the correct directory

cwd = pwd; dirtail = ['gaimc' filesep 'demo'];
+if strcmp(cwd(end-length(dirtail)+1:end),dirtail) == 0
+    error('%s should be executed from %s\n',mfilename,dirtail);
+end
+

initalize the results structure

results=[];
+mex_fast=0; mat_fast=0; mex_std=0; mat_std=0;
+

Connected components

+

To evaluate the performance of the connected components algorithm, we use sets of random graphs.

nrep=30;
+szs=[1 10 100 5000 10000 50000];
+comp_results=[mex_fast mat_fast mex_std mat_std];
+for szi=1:length(szs)
+    fprintf('\n%20s size=%-5i     ', 'scomponents', szs(szi));
+    % Matlab needs 1 iteration to compile the function
+    if szi==2, mex_fast=0; mat_fast=0; mex_std=0; mat_std=0; end
+    for rep=1:nrep
+        fprintf('\b\b\b\b'); fprintf(' %3i', rep);
+        A=sprand(szs(szi),szs(szi),25/szs(szi));
+        At=A'; [rp ci ai]=sparse_to_csr(A); As.rp=rp; As.ci=ci; As.ai=ai;
+        tic; cc1=components(A); mex_std=mex_std+toc;
+        tic; cc2=components(At,struct('istrans',1,'nocheck',1));
+            mex_fast=mex_fast+toc;
+        tic; cc3=scomponents(A); mat_std=mat_std+toc;
+        tic; cc4=scomponents(As); mat_fast=mat_fast+toc;
+        cs1=accumarray(cc1,1,[max(cc1) 1]);
+        cs2=accumarray(cc2,1,[max(cc2) 1]);
+        cs3=accumarray(cc3,1,[max(cc3) 1]);
+        cs4=accumarray(cc4,1,[max(cc4) 1]);
+        if any(cs1 ~= cs2) || any(cs2 ~= cs3) || any(cs2 ~= cs4)
+            error('gaimc:scomponents','incorrect results from scomponents');
+        end
+    end
+    comp_results(end+1,:) = [mex_fast mat_fast mex_std mat_std];
+end
+comp_results=diff(comp_results);
+results(end+1).name='scomponents';
+results(end).mex_fast = mex_fast;
+results(end).mat_fast = mat_fast;
+results(end).mex_std = mex_std;
+results(end).mat_std = mat_std;
+
+         scomponents size=1       30
+         scomponents size=10      30
+         scomponents size=100     30
+         scomponents size=5000    30
+         scomponents size=10000   30
+         scomponents size=50000   30

Dijkstra's algorithm

+

To evaluate the performance of Dijkstra's algorithm, we pick

graphs = {'clr-25-2', 'clr-24-1', 'cs-stanford', ...
+    'minnesota', 'tapir'};
+nrep=30; ntests=100; mex_fast=0; mat_fast=0; mex_std=0; mat_std=0;
+for rep=1:nrep
+    for gi=1:length(graphs)
+        load([graphdir graphs{gi} '.mat']); n=size(A,1);
+        At=A'; [rp ci ai]=sparse_to_csr(A); As.rp=rp; As.ci=ci; As.ai=ai;
+        for ti=1:ntests
+            fprintf([repmat('\b',1,66) '%20s rep=%3i graph=%-20s trial=%4i'], ...
+                'dijkstra',rep,graphs{gi},ti);
+            v=ceil(n*rand(1));
+            tic; d1=dijkstra_sp(A,v); mex_std=mex_std+toc;
+            tic; d2=dijkstra_sp(At,v,struct('istrans',1,'nocheck',1));
+              mex_fast=mex_fast+toc;
+            tic; d3=dijkstra(A,v); mat_std=mat_std+toc;
+            tic; d4=dijkstra(As,v); mat_fast=mat_fast+toc;
+            if any(d1 ~= d2) || any(d2 ~= d3) || any(d3 ~= d4)
+                error('gaimc:dijkstra','incorrect results from dijkstra');
+            end
+        end
+    end
+end
+fprintf('\n');
+results(end+1).name='dijkstra';
+results(end).mex_fast = mex_fast;
+results(end).mat_fast = mat_fast;
+results(end).mex_std = mex_std;
+results(end).mat_std = mat_std;
+
            dijkstra rep= 30 graph=tapir                trial= 100
+

Summarize the results

+

We are going to summarize the results in a bar plot based on the algorithm. Each algorithm is a single bar where the performance + of the mex code is 1. +

nresults=length(results);
+Ystd = zeros(nresults,1);
+Yfast = zeros(nresults,1);
+for i=1:nresults
+    Ystd(i)=results(i).mat_std/results(i).mex_std;
+    Yfast(i)=results(i).mat_fast/results(i).mex_fast;
+end
+bar(1:nresults,[Ystd Yfast]); set(gca,'XTickLabel',{results.name});
+legend('Standard','Fast','Location','Northwest');
+

From this, we see that the connected component codes are about half the speed of the Matlab BGL functions and Dijkstra's is + about 1/4th the speed. This seems unideal, but the code is much more portable and flexible. +

+

The difference between the Standard and Fast results is that the fast results eliminate all data translation for both gaimc + and MatlabBGL and are evaluating the actual algorithm implementation and not any of the data translation components. +

+ +
+ + + \ No newline at end of file diff --git a/demo/html/performance_comparison_simple.png b/demo/html/performance_comparison_simple.png new file mode 100644 index 0000000000000000000000000000000000000000..a2096d9ec83fdfb458f5c5d7d9b7b259b5561452 GIT binary patch literal 1463 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 literal 0 HcmV?d00001
+

Demo of gaimc - 'Graph Algorithms In Matlab Code'

+ +

Matlab includes great algorithms to work with sparse matrices but does provide a reasonable set of algorithms to work with + sparse matrices as graph data structures. My other project -- MatlabBGL -- provides a high-performance solution to this problem + by directly interfacing the Matlab sparse matrix data structure with the Boost Graph Library. That library, however, suffers + from enormous complication because it must be compiled for each platform. The Boost Graph Library heavily uses advanced C++ + features that impair easy portability between platforms. In contrast, the gaimc library is implemented in pure Matlab code, + making it completely portable. +

+

The cost of the portability for this library is a 2-4x slowdown in the runtime of the algorithms as well as significantly + fewer algorithms to choose from. +

+ +

Contents

+
+

Sparse matrices as graphs

+

To store the connectivity structure of the graph, gaimc uses the adjacency matrix of a graph.

+

A graph is represented by a set of vertices and a set of edges between the vertices. Often, we write $G = (V,E)$ to denote the graph, the set of vertices, and the set of edges, respectively. In gaimc, like in my other package MatlabBGL, + we represent graphs with their adjacency matrices. This representation is handy in Matlab because Matlab is rather efficient + at working with the large sparse matrices that typically arise as adjacency matrices. +

+

To convert from $G=(V,E)$ to an adjacency matrix, we identify each vertex with a row of the matrix via a bijective map. The adjacency matrix is then + a $|V| \times |V|$ matrix called A. The entry A(i,j) = 1 for any edge between in $E$ and 0 otherwise. Let's look at an example. +

load_gaimc_graph('bfs_example');
+graph_draw(A,xy,'labels',labels)
+full(A)
+labels'
+
+ans =
+
+     0     1     0     0     1     0     0     0
+     1     0     0     0     0     1     0     0
+     0     0     0     1     0     1     1     0
+     0     0     1     0     0     0     0     1
+     1     0     0     0     0     0     0     0
+     0     1     1     0     0     0     1     0
+     0     0     1     0     0     1     0     1
+     0     0     0     1     0     0     1     0
+
+
+ans = 
+
+    'r'    's'    't'    'u'    'v'    'w'    'x'    'y'
+
+

This output means that vertex 'r' is row 1, vertex 's' is row 2 and because A(1,2) = 1, then there is an edge between them, + just like in the picture. +

+

One funny property is that A(2,1) = 1 too! So we actually have to store each edge twice in the adjacency matrix. This might + seem wasteful, but its hard to avoid as I've learned while working on graph algorithms. So don't worry about it! It also + makes the generalization to directed graphs (below) easy. +

+

For more information about the adjacency matrix representation of a graph, see a standard book on graph algorithms.

+

Weighted and directed graphs

+

Our previous case handled the situation for undirected graphs only. To encode weighted and directed graphs, we use weighted + and non-symmetric adjacency matrices. +

+

For a weighted matrix, A(i,j) = distance between i and j for most of the algorithms in gaimc. But A(i,j) = 0 means there + is no edge, and so sometimes things can get a little tricky to get what you want. +

+

For a directed graph, just set A(i,j) ~= A(j,i). The adjacency matrix won't be symmetric, but that's what you want!

+

To understand more, explore the examples or read up on adjacency matrices in graph theory books.

+

Loading helper

+

To make loading our sample graphs easy, gaimc defines it's own function to load graphs.

load_gaimc_graph('dfs_example'); % loads one of our example graphs
+whos
+
  Name                    Size                  Bytes  Class      Attributes
+
+  A                       9x9                     416  double     sparse    
+  Acc                     5x5                     176  double     sparse    
+  As                      1x1                40792464  struct               
+  At                  50000x50000            20395704  double     sparse    
+  D                       5x5                     200  double               
+  D2                      5x5                     200  double               
+  T                     456x456                 18216  double     sparse    
+  X                    2730x1                   21840  double               
+  Y                    2730x1                   21840  double               
+  ai                1249731x1                 9997848  double               
+  aj                  71959x1                  575672  double               
+  ans                     1x8                     912  cell                 
+  ati               1249731x1                 9997848  double               
+  ax                      1x1                       8  double               
+  cc                      9x1                      72  double               
+  cc1                 50000x1                  400000  double               
+  cc2                 50000x1                  400000  double               
+  cc3                 50000x1                  400000  double               
+  cc4                 50000x1                  400000  double               
+  ccfs                 2642x1                   21136  double               
+  ci                1249731x1                 9997848  double               
+  cn                   2642x1                   21136  double               
+  comp_results            6x4                     192  double               
+  cp                  50001x1                  400008  double               
+  cs1                     1x1                       8  double               
+  cs2                     1x1                       8  double               
+  cs3                     1x1                       8  double               
+  cs4                     1x1                       8  double               
+  cwd                     1x28                     56  char                 
+  d                     456x1                    3648  double               
+  d1                   1024x1                    8192  double               
+  d2                   1024x1                    8192  double               
+  d3                   1024x1                    8192  double               
+  d4                   1024x1                    8192  double               
+  de                  71959x1                  575672  double               
+  dest                    1x1                       8  double               
+  dirtail                 1x10                     20  char                 
+  dt                      8x1                      64  double               
+  f                       9x1                       9  logical              
+  faceColors              1x1                    1904  struct               
+  gi                      1x1                       8  double               
+  graphdir                1x10                     20  char                 
+  graphs                  1x5                     642  cell                 
+  i                       1x1                       8  double               
+  ind                     1x1                       8  double               
+  labels                  9x1                    1026  cell                 
+  lat                  9865x1                   78920  double               
+  lax                     1x1                       8  double               
+  long                 9865x1                   78920  double               
+  mat_fast                1x1                       8  double               
+  mat_std                 1x1                       8  double               
+  matlabbgldir            1x20                     40  char                 
+  mex_fast                1x1                       8  double               
+  mex_std                 1x1                       8  double               
+  n                       1x1                       8  double               
+  nrep                    1x1                       8  double               
+  ntests                  1x1                       8  double               
+  path                    1x3                      24  double               
+  pred                    1x456                  3648  double               
+  rep                     1x1                       8  double               
+  results                 1x3                    2140  struct               
+  ri                1249731x1                 9997848  double               
+  rp                  50001x1                  400008  double               
+  rst                     1x1                       8  double               
+  rt                  71959x1                  575672  double               
+  si                  71959x1                  575672  double               
+  source                  1x83                    166  char                 
+  start                   1x1                       8  double               
+  states                 49x1                  190442  struct               
+  szi                     1x1                       8  double               
+  szs                     1x6                      48  double               
+  te                  71959x1                  575672  double               
+  ti                      1x1                       8  double               
+  u                       1x1                       8  double               
+  v                       1x1                       8  double               
+  val                     1x1                       8  double               
+  xy                      9x2                     144  double               
+
+

This helps make our examples work regardless of where the current directory lies.

+

Search algorithms

+

The two standard graph search algorithms are depth first search and breadth first search. This library implements both.

+

Load the example matrix from the Boost Graph Library

load_gaimc_graph('dfs_example');
+figure(1); graph_draw(A,xy,'labels',labels);
+

Run a depth first search. The output records the distance to the other vertices, except where the vertices are not reachable + starting from the first node A. +

d=dfs(A,1)
+
+d =
+
+     0
+     1
+     3
+     3
+     2
+     4
+    -1
+    -1
+    -1
+
+

From this example, we see that vertices a-f are reachable from vertex a, but that verice g-i are not reachable. Given the + of the edges, this makes sense. +

+

Let's look at breadth first search too, using a different example.

load_gaimc_graph('bfs_example');
+figure(1); clf; graph_draw(A,xy,'labels',labels);
+

The breadth first search algorithm records the distance from the starting vertex to each vertex it visits in breadth first + order. This means it visits all the vertices in order of their distance from the starting vertex. The d output records the + distance, and the dt output records the step of the algorithm when the breadth first search saw the node. +

[d dt] = bfs(A,2);
+% draw the graph where the label is the "discovery time" of the vertex.
+figure(1); clf; graph_draw(A,xy,'labels',num2str(dt));
+

Notice how the algorithm visits all vertices one edge away from the start vertex (0) before visiting those two edges away.

+

Shortest paths

+

In the previous two examples, the distance between vertices was equivalent to the number of edges. Some graphs, however, + have specific weights, such as the graph of flights between airports. We can use this information to build information about + the shortest path between two nodes in a network. +

% Find the minimum travel time between Los Angeles (LAX) and
+% Rochester Minnesota (RST).
+load_gaimc_graph('airports')
+A = -A; % fix funny encoding of airport data
+lax=247; rst=355;
+
+[d pred] = dijkstra(A,lax); % find all the shorest paths from Los Angeles.
+
+fprintf('Minimum time: %g\n',d(rst));
+% Print the path
+fprintf('Path:\n');
+path =[]; u = rst; while (u ~= lax) path=[u path]; u=pred(u); end
+fprintf('%s',labels{lax});
+for i=path; fprintf(' --> %s', labels{i}); end, fprintf('\n');
+
Minimum time: 244
+Path:
+Los Angeles, CA --> Minneapolis/St Paul, MN --> Rochester, MN
+

Minimum spanning trees

+

A minimum spanning tree is a set of edges from a graph that ...

+

This demo requires the mapping toolbox for maximum effect, but we'll do okay without it.

+

Our data comes from a graph Brendan Frey prepared for his affinity propagation clustering tool. For 456 cities in the US, + we have the mean travel time between airports in those cities, along with their latitude and longitude. +

load_gaimc_graph('airports')
+

For some reason, the data is stored with the negative travel time between cities. (I believe this is so that closer cities + have larger edges between them.) But for a minimum spanning tree, we want the actual travel time between cities. +

A = -A;
+

Now, we just call MST and look at the result. T = mst_prim(A); This command means we can't run the demo, so it's commented + out. +

+

Oops, travel time isn't symmetric! Let's just pick the longest possible time.

A = max(A,A');
+T = mst_prim(A);
+sum(sum(T))/2 % total travel time in tree
+
+ans =
+
+   (1,1)          24550
+
+

Well, the total weight isn't that helpful, let's look at the data instead. +

clf;
+gplot(T,xy);
+

Hey! That looks like the US! You can see regional airports and get some sense of the overall connectivity.

+

Connected components

+

The connected components of a network determine which parts of the network are reachable from other parts. One of your first + questions about any network should generally be: is it connected? +

+

There are two types of connected components: components and strongly connected components. gaimc only implements an algorithm + for the latter case, but that's okay! It turns out it computes exactly the right thing for connected components as well. + The difference only occurs when the graph is undirected vs. directed. +

load_gaimc_graph('dfs_example')
+graph_draw(A,xy)
+

This picture shows there are 3 strongly connected components and 2 connected components

% get the number of strongly connected components
+max(scomponents(A))
+
+ans =
+
+     3
+
+
% get the number of connected components
+max(scomponents(A|A'))  % we make the graph symmetric first by "or"ing each entry
+
+ans =
+
+     2
+
+

Let's look at the vertices in the strongly connected components

cc = scomponents(A)
+
+cc =
+
+     2
+     2
+     2
+     1
+     2
+     2
+     3
+     3
+     3
+
+

The output tells us that vertices 1,2,3,5,6 are in one strong component, vertex 4 is it's own strong component, and vertices + 7,8,9 are in another one. Remember that a strong component is all the vertices mutually reachable from a given vertex. If + you start at vertex 4, you can't get anywhere else! That's why it is in a different component than vertices 1,2,3,5,6. +

+

We also have a largest_component function that makes it easy to just get the largest connected component.

clf;
+[Acc,f] = largest_component(A);
+graph_draw(Acc,xy(f,:))
+

The filter variable f, tells us which vertices in the original graph made it into the largest strong component. We can just + apply that filter to the coordinates xy and reuse them for drawing the graph! +

+

Statistics

+

Graph statistics are just measures that indicate a property of the graph at every vertex, or at every edges. Arguably the + simplest graph statistic would be the average vertex degree. Because such statistics are easy to compute with the adjaceny + matrix in Matlab, they do not have special functions in gaimc. +

+

Load a road network to use for statistical computations

load_gaimc_graph('minnesota');
+gplot(A,xy);
+

Average vertex degree

d = sum(A,2);
+mean(d)
+
+ans =
+
+   (1,1)       2.5034
+
+

So the average number of roads at any intersection is 2.5. My guess is that many roads have artificial intersections in the + graph structure that do not correspond to real intersections. Try validating that hypothesis using the library! +

+

Average clustering coefficients

ccfs = clustercoeffs(A);
+mean(ccfs)
+% The average clustering coefficient is a measure of the edge density
+% throughout the graph.  A small value indicates that the network has few
+% edges and they are well distributed throughout the graph.
+
+ans =
+
+    0.0160
+
+

Average core numbers

cn = corenums(A);
+mean(cn)
+
+ans =
+
+    1.9463
+
+

Efficient repetition

+

Every time a gaimc function runs, it converts the adjacency matrix into a set of compressed sparse row arrays. These arrays + yield efficient access to the edges of the graph starting at a particular vertex. For many function calls on the same graph, + this conversion process slows the algorithms. Hence, gaimc also accepts pre-converted input, in which case it skips it's + conversion. +

+

Let's demonstrate how this works by calling Dijkstra's algorithm to compute the shortest paths between all vertices in the + graph. The Floyd Warshall algorithm computes these same quantities more efficiently, but that would just be one more algorithm + to implement and maintain. +

+

Load and convert the graph.

load_gaimc_graph('all_shortest_paths_example');
+A = spfun(@(x) x-min(min(A))+1,A); % remove the negative edges
+As = convert_sparse(A);
+

Now, we'll run Dijkstra's algorithm for every vertex and save the result On my 2GHz laptop, this takes 0.000485 seconds.

n = size(A,1);
+D = zeros(n,n);
+tic
+for i=1:n
+    D(i,:) = dijkstra(As,i);
+end
+toc
+
Elapsed time is 0.000304 seconds.
+

Let's try it without the conversion to see if we can notice the difference in speed. On my 2GHz laptop, this takes 0.001392 + seconds. +

D2 = zeros(n,n);
+tic
+for i=1:n
+    D2(i,:) = dijkstra(A,i);
+end
+toc
+
Elapsed time is 0.000541 seconds.
+

And just to check, let's make sure the output is the same.

isequal(D,D2)
+
+ans =
+
+     1
+
+
+