| package org.eclipse.stem.graphsynchronizer.util; |
| |
| /******************************************************************************* |
| * Copyright (c) 2011 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.resource.impl.ExtensibleURIConverterImpl; |
| import org.eclipse.stem.graphsynchronizer.Activator; |
| |
| public class SubCountryGraphPartitionPlanner { |
| |
| static int NUM_COUNTRIES = -1; |
| static int NUM_LEVEL_1_IDS = -1; |
| static int[][] level0CommonBorderMatrix = null; |
| static int[][] level1CommonBorderMatrix = null; |
| static final String STATS_FILE_0 = "worldPartioningStats.csv"; //$NON-NLS-1$ |
| static final String STATS_FILE_1 = "subCountryPartioningStats.csv"; //$NON-NLS-1$ |
| static final String CODES_FILE = "countryCodeMap.csv"; //$NON-NLS-1$ |
| |
| @SuppressWarnings("nls") |
| private static final String URI_PREFIX = "platform:/plugin/org.eclipse.stem.data.geography/resources/data/statistics/"; |
| //private static final String URI_PREFIX = "/Users/jhkauf/Documents/workspace/org.eclipse.stem.data.geography/resources/data/statistics/"; |
| |
| private static final URI CODES_FILE_URI = URI.createURI(URI_PREFIX + CODES_FILE); |
| |
| private static final URI STATS_FILE_0_URI = URI.createURI(URI_PREFIX + STATS_FILE_0); |
| |
| private static final URI STATS_FILE_1_URI = URI.createURI(URI_PREFIX + STATS_FILE_1); |
| |
| |
| static Map<String,String> regionCode2to3 = new HashMap<String,String>(); |
| static Map<String,String> regionCode3to2 = new HashMap<String,String>(); |
| |
| static final List<Country> worldCountryList = new ArrayList<Country>(); |
| static final Map<String,Country> worldCountryMap = new HashMap<String,Country>(); |
| |
| static final List<Region> worldStateList = new ArrayList<Region>(); |
| static final List<String> levelOneIDList = new ArrayList<String>(); |
| |
| static final Map<String,Region> worldSubCountryMap = new HashMap<String,Region>(); |
| static final double DELTA_TOLLERANCE = 0.2; |
| static double SIZE_MISSMATCH_TOLLERANCE = 1.0+DELTA_TOLLERANCE; |
| |
| /** |
| * |
| */ |
| public SubCountryGraphPartitionPlanner() { |
| // step 0, regenerate the stats every time in case the STEM data changes |
| //WorldPartitioningStats wps = new WorldPartitioningStats(); |
| |
| |
| // then prepare to do the plan |
| mapCodes(); |
| mapCountryStats(); |
| mapSubCountryStats(); |
| } |
| |
| /** |
| * Main method for testing only |
| * @param args |
| */ |
| @SuppressWarnings("nls") |
| public static void main(String[] args) { |
| |
| /* |
| * For testing only |
| */ |
| String[] test = { |
| "AFG","AGO","AIA","ALA","ALB","AND","ANT","ARE","ARG","ARM","ASM","ATA","ATF","ATG", |
| "AUS","AUT","AZE","BDI","BEL","BEN","BFA","BGD","BGR","BHR","BHS","BIH","BLR","BLZ", |
| "BMU","BOL","BRA","BRB","BRN","BTN","BVT","BWA","CAF","CAN","CCK","CHE","CHL","CHN", |
| "CIV","CMR","COD","COG","COK","COL","COM","CPV","CRI","CUB","CXR","CYM","CYP","CZE", |
| "DEU","DJI","DMA","DNK","DOM","DZA","ECU","EGY","ERI","ESH","ESP","EST","ETH","FIN", |
| "FJI","FLK","FRA","FRO","FSM","GAB","GBR","GEO","GGY","GHA","GIB","GIN","GLP","GMB", |
| "GNB","GNQ","GRC","GRD","GRL","GTM","GUF","GUM","GUY","HKG","HMD","HND","HRV","HTI", |
| "HUN","IDN","IMN","IND","IOT","IRL","IRN","IRQ","ISL","ISR","ITA","JAM","JEY","JOR", |
| "JPN","KAZ","KEN","KGZ","KHM","KIR","KNA","KOR","KWT","LAO","LBN","LBR","LBY","LCA", |
| "LIE","LKA","LSO","LTU","LUX","LVA","MAC","MAR","MCO","MDA","MDG","MDV","MEX","MHL", |
| "MKD","MLI","MLT","MMR","MNE","MNG","MNP","MOZ","MRT","MSR","MTQ","MUS","MWI","MYS", |
| "MYT","NAM","NCL","NER","NFK","NGA","NIC","NIU","NLD","NOR","NPL","NRU","NZL","OMN", |
| "PAK","PAN","PCN","PER","PHL","PLW","PNG","POL","PRI","PRK","PRT","PRY","PSE","PYF", |
| "QAT","REU","ROU","RUS","RWA","SAU","SDN","SEN","SGP","SGS","SHN","SJM","SLB","SLE", |
| "SLV","SMR","SOM","SPM","SRB","STP","SUR","SVK","SVN","SWE","SWZ","SYC","SYR","TCA", |
| "TCD","TGO","THA","TJK","TKL","TKM","TLS","TMP","TON","TTO","TUN","TUR","TUV","TWN", |
| "TZA","UGA","UKR","UMI","URY","USA","UZB","VAT","VCT","VEN","VGB","VIR","VNM","VUT", |
| "WLF","WSM","YEM","ZAF","ZMB","ZWE" }; |
| |
| |
| Set<String> testSet = new HashSet<String>(); |
| for (int i = 0; i < test.length; i ++) { |
| testSet.add(test[i]); |
| } |
| |
| SubCountryGraphPartitionPlanner subCtryPartionPlanner = new SubCountryGraphPartitionPlanner(); |
| |
| int numServers = 7; |
| |
| // DEBUG |
| // subCtryPartionPlanner.checkSumCountryStats(); |
| |
| final List<PartitionGroup> initialPlan = subCtryPartionPlanner.getInitialCountryLevelPlan(numServers, testSet); |
| //final List<PartitionGroup> initialPlan = subCtryPartionPlanner.getPlan(numServers, testSet); |
| List<PartitionGroup> plan = subCtryPartionPlanner.relaxPartitionPlan(initialPlan); |
| |
| List<Set<String>> planCodes = subCtryPartionPlanner.planAsCodeStrings(plan); |
| |
| Activator.logInformation("planning complete.... assignments are:"); |
| int allNodes = 0; |
| for(int i = 0; i < plan.size(); i ++) { |
| |
| PartitionGroup pg = plan.get(i); |
| Set<String> regionSet = planCodes.get(i); |
| |
| // checksum |
| Set<Region> regionS = pg.regionGroup; |
| for(Region r:regionS) { |
| allNodes += r.numNodes; |
| } |
| |
| double surface = pg.countUnresolvedBorderEdges(); |
| int totalNodes = pg.getTotalNodes(); |
| double surfaceToVolume = surface/(double)totalNodes; |
| // *** Activator.logInformation(" "); |
| Activator.logInformation("server number: "+i+" size is ["+totalNodes+"] external Edges = "+(int)surface+" surfaceToVolumeRatio= "+surfaceToVolume); |
| |
| String s = ""; |
| int icount = 0; |
| String[] rgns = regionSet.toArray(new String[regionSet.size()]); |
| Arrays.sort(rgns); |
| |
| for(String region: rgns) { |
| s += region+" "; |
| icount ++; |
| if (icount %25 == 0) s += "\n "; |
| }// for all countries on group |
| // ***** Activator.logInformation(" "+s); |
| |
| }// for all groups |
| Activator.logInformation("total nodeCount = "+allNodes); |
| |
| //now the the matrix of message size group to group |
| int[][] pgMatrix = getPartitionGroupBorderMatrix(plan); |
| Activator.logInformation(""); |
| Activator.logInformation(""); |
| Activator.logInformation("PartitionGroup Matrix"); |
| Activator.logInformation(""); |
| for (int i = 0; i < plan.size(); i ++) { |
| String s = ""; |
| for (int j = 0; j < plan.size(); j ++) { |
| if(pgMatrix[i][j]<10) s += " "; |
| if(pgMatrix[i][j]<100) s += " "; |
| s += pgMatrix[i][j]+" "; |
| } |
| Activator.logInformation(s); |
| } |
| } |
| |
| /** |
| * Generate a partition plan for a collection of level 1 regions to run |
| * 0. validate the number of servers |
| * 1. initialize an empty plan for numServers |
| * 2. add all level 1 regions to an unassigned set |
| * 3. get the level 1 regions sorted large to small |
| * 4. GET the total number of nodes for the ENTIRE collection to run |
| * 5. estimate the approximate target size per Partition Group |
| * 6. add the numServers largest level 1 regions in order large to small |
| * 6.a. find the numServers largest countries in order large to small |
| * 6.b.pick the largest regions inside the next largest country |
| * 7. we have assigned numServers level 1 regions |
| * 8. iterate through the unassigned level 1 regions until we have no more |
| * 9. check max pg size here |
| * 10. get the highest surface area for all candidates wrt THIS group pg |
| * 11. is it the overall Largest?? |
| * 12. if we are not reducing surface area we need to change the plan |
| * 13. assign the selected region |
| * 14. remove it from the unassigned list |
| * |
| * @param numServers |
| * @param unassignedSet |
| * @return |
| */ |
| @SuppressWarnings("nls") |
| public List<PartitionGroup> getPlan(int numServers, final Set<String> countries) { |
| |
| //0. The regions are a list of countries |
| // but we really want to study admin 1 regions |
| // so for each country: |
| // i: Get the two letter code from the three letter code |
| // ii: From the levelOneIDList add all the level one ids |
| // ii: or, if there is only the level 0 id add that. |
| Set<String> regions = new HashSet<String>(); |
| for(String threeLetterID: countries) { |
| |
| String twoLetterPrefix = regionCode3to2.get(threeLetterID); |
| |
| for(int i = 0; i < levelOneIDList.size(); i ++) { |
| String levelOneID = levelOneIDList.get(i); |
| if((twoLetterPrefix!=null) && (levelOneID.indexOf(twoLetterPrefix)>=0)) regions.add(levelOneID); |
| if(levelOneID.equalsIgnoreCase(threeLetterID)) regions.add(threeLetterID); |
| } |
| |
| } |
| // we now have the full region set in regions |
| |
| if(numServers > regions.size()) { |
| numServers = regions.size(); |
| Activator.logInformation("CountryGraphPatitionPlanner.getPlan(): Warning, number of servers can not exceed number of countries. Using "+numServers+" servers for now."); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| |
| |
| //1. init an empty plan |
| List<PartitionGroup> partitionPlan = new ArrayList<PartitionGroup>(); |
| for(int i = 0; i < numServers; i ++) { |
| PartitionGroup partitionGroup = new PartitionGroup(); // set of three letter region codes = plan for one server; |
| partitionPlan.add(partitionGroup); |
| } |
| |
| // 2. add all regions to an unassigned set |
| Set<String> unassignedSet = new HashSet<String>(); |
| unassignedSet.addAll(regions); |
| |
| // 3. get the regions sorted large to small |
| Region[] unassignedRegions = getSortedRegions(unassignedSet); |
| |
| //4. GET the total number of nodes for the ENTIRE collection to run |
| int totalProblemSize = 0; |
| for(int i = 0; i < unassignedRegions.length; i ++) { |
| totalProblemSize+= unassignedRegions[i].numNodes; |
| } |
| |
| // 5. estimate the approximate target size per Partition Group |
| Activator.logInformation("CountryGraphPatitionPlanner.getPlan(): total problem size = "+totalProblemSize); |
| double targetGroupSizeD = totalProblemSize/numServers; // approximate target |
| Activator.logInformation("CountryGraphPatitionPlanner.getPlan(): IDEAL target partition size = "+Math.round(targetGroupSizeD)); |
| targetGroupSizeD*= SIZE_MISSMATCH_TOLLERANCE; |
| int targetGroupSize = (int)Math.round(targetGroupSizeD); |
| Activator.logInformation("CountryGraphPatitionPlanner.getPlan(): Planning for max target partition size ~= "+targetGroupSize); |
| |
| // 6. seed the first numServer Regions |
| // 6.a. find the numServers largest countries in order large to small |
| SubCountryGraphPartitionPlanner.Country[] parents = getSortedCountries(worldCountryList); |
| Arrays.sort(parents); |
| |
| |
| |
| for(int i = 0; i < numServers; i ++) { |
| // the group to seed |
| PartitionGroup partitionGroup = partitionPlan.get(i); |
| String nextLargestCountry = parents[i].threeLetterCode; |
| |
| Region[] unassigned = getSortedRegions(unassignedSet); |
| // 6.b.pick A region inside the next largest country |
| Region seedRegion = null; |
| for(int j = 0; j < unassigned.length; j ++) { |
| seedRegion = unassigned[j]; |
| if(seedRegion.parentCountryCode.equalsIgnoreCase(nextLargestCountry)) { |
| partitionGroup.add(seedRegion); |
| Activator.logInformation(""+i+": seeding with "+seedRegion.regionID+" inside country "+nextLargestCountry); |
| unassignedSet.remove(seedRegion); |
| } |
| |
| } |
| |
| |
| |
| |
| } |
| |
| // 7. we have assigned numServers regions |
| |
| |
| // 8. iterate through the unassigned regions until we have no more |
| double progress = 0; |
| |
| while(unassignedSet.size() > 0) { |
| |
| if(progress%100==0) { |
| Activator.logInformation(" "+unassignedSet.size()+" regions remaining to be assigned"); |
| } |
| progress ++; |
| |
| Region[] regionsToProcess=getSortedRegions(unassignedSet); |
| |
| // for each partition group |
| |
| Region regionToAssign = null; |
| PartitionGroup destinationGroup = null; |
| double minFactor = Double.MAX_VALUE; |
| |
| for(int i = 0; i < partitionPlan.size(); i ++) { |
| // iterate through every unassigned region |
| PartitionGroup pg = partitionPlan.get(i); |
| pg.nextCandidateMember=null; |
| |
| // 9. check max pg size here |
| if(pg.getTotalNodes() < targetGroupSize) { |
| // 10. get the highest surface area for all candidates wrt THIS group pg |
| for(Region candidate: regionsToProcess) { |
| // for every OTHER group |
| |
| double worstCaseLoadThisCandidateThisPlan = Double.MIN_VALUE; |
| |
| for(int j = 0; j < partitionPlan.size(); j ++) { |
| // iterate through every unassigned region |
| if(j!=i) { |
| PartitionGroup pgOther = partitionPlan.get(j); |
| double load = getCommunicationsLoadFactor(candidate, pgOther.regionGroup, pg.regionGroup) ; |
| if(load > worstCaseLoadThisCandidateThisPlan) { |
| worstCaseLoadThisCandidateThisPlan = load; |
| }// if new best |
| }// if not this group |
| }// for all other planGroups |
| |
| // is this a new best case |
| if(worstCaseLoadThisCandidateThisPlan < minFactor) { |
| minFactor = worstCaseLoadThisCandidateThisPlan; |
| regionToAssign=candidate; |
| destinationGroup=pg; |
| } |
| |
| }// for all unassigned regions |
| |
| }// if partition group is not too big already |
| }// for all partitionGroups |
| |
| //11. add the candidate |
| if((regionToAssign !=null)&&(destinationGroup != null)) { |
| destinationGroup.add(regionToAssign); |
| unassignedSet.remove(regionToAssign.regionID); |
| } |
| |
| |
| |
| if(unassignedSet.size()==0) break; |
| |
| |
| }// WHILE we have more unassigned regions to process |
| |
| return partitionPlan; |
| } |
| |
| /** |
| * |
| * @param numServers |
| * @param countries |
| * @return |
| */ |
| @SuppressWarnings("nls") |
| public List<PartitionGroup> getInitialCountryLevelPlan(int numServers, final Set<String> countries) { |
| |
| Activator.logInformation("1. Getting initial Country Level Plan"); |
| // 1. Get an INITIAL plan using the country level plannter |
| CountryGraphPartitionPlanner cgpp = new CountryGraphPartitionPlanner(); |
| List<CountryGraphPartitionPlanner.PartitionGroup> initialPlan = cgpp.getPlan(numServers, countries); |
| List<Set<String>> initialPlanCountrySet = cgpp.planAsCodeStrings(initialPlan); |
| |
| // we now have the full region set in regions |
| if(numServers > initialPlanCountrySet.size()) { |
| numServers = initialPlanCountrySet.size(); |
| Activator.logInformation("CountryGraphPatitionPlanner.getPlan(): Warning, number of servers can not exceed number of countries. Using "+numServers+" servers for now."); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| Activator.logInformation("2. Executing initial Country Level Plan by level 1 regions"); |
| |
| // 2. execute this plan by the level one regions regions |
| // init the plan based on the country assignments |
| int partitionCount = 0; |
| List<PartitionGroup> partitionPlan = new ArrayList<PartitionGroup>(); |
| for(Set<String> countrySet:initialPlanCountrySet) { |
| // define a level 1 partition group for this set |
| PartitionGroup partitionGroup = new PartitionGroup(); // set of three letter region codes = plan for one server; |
| partitionPlan.add(partitionGroup); |
| Activator.logInformation(""); |
| System.out.print("Partition "+partitionCount+" adding "); |
| partitionCount++; |
| // for every country in the plan |
| for(String threeLetterID: countrySet) { |
| |
| System.out.print(" "+threeLetterID); |
| String twoLetterPrefix = regionCode3to2.get(threeLetterID); |
| for(int i = 0; i < levelOneIDList.size(); i ++) { |
| String levelOneID = levelOneIDList.get(i); |
| if((twoLetterPrefix!=null) && (levelOneID.indexOf(twoLetterPrefix)==0)) { |
| Region region = worldSubCountryMap.get(levelOneID); |
| partitionGroup.add(region); |
| } |
| // or.... if only the country node |
| if(levelOneID.equalsIgnoreCase(threeLetterID)) { |
| Region region = worldSubCountryMap.get(levelOneID); |
| partitionGroup.add(region); |
| } |
| } |
| } |
| |
| } |
| return partitionPlan; |
| } |
| |
| |
| /** |
| * |
| * @param numServers |
| * @param countries |
| * @return |
| */ |
| public List<PartitionGroup> relaxPartitionPlan(List<PartitionGroup> partitionPlan) { |
| |
| |
| |
| //3. get an accurate count of the total nodes |
| int problemSize = 0; |
| for(int i = 0; i < partitionPlan.size(); i ++) { |
| PartitionGroup pg = partitionPlan.get(i); |
| problemSize += pg.getTotalNodes(); |
| } |
| |
| double idealTargetSize = problemSize/partitionPlan.size(); |
| // 4. Relaxation. |
| |
| |
| //int pickIndex = getLargestGroupIndex(partitionPlan); |
| //PartitionGroup largeGroup = partitionPlan.get(pickIndex); |
| |
| SIZE_MISSMATCH_TOLLERANCE = 1.0+DELTA_TOLLERANCE; |
| for(int outer = 0; outer < 500; outer ++) { |
| // 4.a. do an initial sort of the partitionGroups by size |
| PartitionGroup[] sortedPlans = partitionPlan.toArray(new PartitionGroup[partitionPlan.size()]); |
| Arrays.sort(sortedPlans); |
| |
| |
| for(int inner = 0; inner < 10; inner ++) { |
| // iterate through them |
| for(int i = 0; i < sortedPlans.length; i ++) { |
| |
| PartitionGroup currentGroup= sortedPlans[i]; |
| int planSize = currentGroup.getTotalNodes(); |
| |
| // for each group with size > target size.... |
| if(planSize > (idealTargetSize*SIZE_MISSMATCH_TOLLERANCE)) { |
| // try to make a move - this really is a largeGroup |
| int pickIndex = i; // a large plan |
| double maxExternalArea = Double.MIN_VALUE; |
| Region regionToMove = null; |
| PartitionGroup groupToMoveTo = null; |
| //int destinationIndex = 0; |
| // 4a. For each region in the largest group |
| for(Region candidate: currentGroup.regionGroup) { |
| //4b. Find the other partition group with the LARGEST connections to that region |
| for(int j = 0; j < partitionPlan.size(); j ++) { |
| if(j!= pickIndex) { |
| // other groups only |
| PartitionGroup otherGroup = partitionPlan.get(j); |
| if(otherGroup.getTotalNodes()<=idealTargetSize){ |
| // small plan |
| double load = getCommunicationsLoadFactor(candidate, otherGroup.regionGroup, currentGroup.regionGroup) ; |
| if(load > maxExternalArea) { |
| maxExternalArea = load; |
| regionToMove = candidate; |
| groupToMoveTo = otherGroup; |
| //destinationIndex = j; |
| }// if new best |
| }// if small plan |
| |
| }// if other group |
| } // for j other partitionGroups |
| }// for all regions in group |
| |
| |
| // 4c. Make the move |
| if((regionToMove !=null)&&(groupToMoveTo != null)) { |
| currentGroup.regionGroup.remove(regionToMove); |
| groupToMoveTo.regionGroup.add(regionToMove); |
| } |
| } // if plan size >> avg |
| else if(planSize < (idealTargetSize/SIZE_MISSMATCH_TOLLERANCE)) { |
| |
| // if really small find the best outside region to add |
| PartitionGroup groupToMoveTo = currentGroup; |
| PartitionGroup groupToMoveFrom = null; |
| Region regionToMove = null; |
| int maxArea = -1; |
| int pickIndex = i; |
| |
| // iterate through all the OTHER groups |
| for(int j = 0; j < partitionPlan.size(); j ++) { |
| if(j!= pickIndex) { |
| // other groups only |
| PartitionGroup otherGroup = partitionPlan.get(j); |
| if(otherGroup.getTotalNodes()>(idealTargetSize*SIZE_MISSMATCH_TOLLERANCE)){ |
| for(Region candidate: otherGroup.regionGroup) { |
| int area = groupToMoveTo.getSurfaceArea(candidate); |
| if(area > maxArea) { |
| regionToMove = candidate; |
| groupToMoveFrom = otherGroup; |
| }// if best candidate |
| } //for all regions in other group |
| |
| }//if other group is large |
| }//if other group |
| }// for all groups |
| if((regionToMove !=null)&&(groupToMoveFrom != null)) { |
| groupToMoveFrom.regionGroup.remove(regionToMove); |
| groupToMoveTo.regionGroup.add(regionToMove); |
| } |
| |
| }// if plan size << average |
| |
| }// for all plans by size |
| |
| |
| }// relax inner loop |
| }// relax outer loop |
| |
| |
| |
| |
| return partitionPlan; |
| } // relaxPlanByCountries |
| |
| |
| /** |
| * We want the minimum communication cost with OTHER RegionGroups and the maximum surface area reduction with THIS one. |
| * we compute the ratio surfaceAreaOTHER/surfaceAreaTHIS. Bigger values are better to move out. |
| * @param regionsToProcess |
| * @return |
| */ |
| public double getCommunicationsLoadFactor(Region candidate, Set<Region> outsideRegions, Set<Region> homeRegions) { |
| double factor = Double.MIN_VALUE; |
| |
| double outSideSurfaceArea = 0; |
| // for each region in OutSide group |
| for(Region region: outsideRegions) { |
| outSideSurfaceArea += level1CommonBorderMatrix[candidate.index][region.index]; |
| } |
| |
| double inSideSurfaceArea = 0; |
| // for each region in OutSide group |
| for(Region region: homeRegions) { |
| inSideSurfaceArea += level1CommonBorderMatrix[candidate.index][region.index]; |
| } |
| |
| |
| // weight the result by the volume of the region |
| //surfaceAreaTHIS *= candidate.numNodes; |
| if(inSideSurfaceArea <=0 ) inSideSurfaceArea = 0.01; // avoid divide by zero |
| if(outSideSurfaceArea ==0 ) outSideSurfaceArea = 0.1; // weight only by 0.1/surfaceAreaTHIS; |
| double ratio = outSideSurfaceArea/inSideSurfaceArea; |
| |
| if(ratio > factor) { |
| factor = ratio; |
| } |
| |
| return factor; |
| } |
| |
| |
| /** |
| * |
| * @param partitionPlan |
| * @return |
| */ |
| @SuppressWarnings("nls") |
| public int getLargestGroupIndex(List<PartitionGroup> partitionPlan) { |
| int pickIndex = 0; |
| int maxSize = -1; |
| for(int i = 0; i < partitionPlan.size(); i ++) { |
| PartitionGroup pg = partitionPlan.get(i); |
| int numNodes = pg.getTotalNodes(); |
| if(numNodes > maxSize) { |
| maxSize = numNodes; |
| pickIndex = i; |
| } |
| } |
| Activator.logInformation("**** choosing "+pickIndex+" size = "+maxSize); |
| return pickIndex; |
| }//getLargestGroup |
| |
| /** |
| * |
| * @param partitionPlan |
| * @return |
| */ |
| public List<Set<String>> planAsCodeStrings(List<PartitionGroup> partitionPlan) { |
| List<Set<String>> planList = new ArrayList<Set<String>>(); |
| for(int i = 0; i < partitionPlan.size(); i ++) { |
| // iterate through every unassigned region |
| PartitionGroup pg = partitionPlan.get(i); |
| planList.add(pg.getRegionCodes()); |
| }// for all partitionGroups |
| |
| return planList; |
| } |
| |
| |
| |
| /** |
| * gets the smallest current |
| * @param partitionPlan |
| * @return |
| */ |
| public int getSmallestGroupIndex(List<PartitionGroup> partitionPlan) { |
| int smallest = Integer.MAX_VALUE; |
| int index = -1; |
| for(int i = 0; i < partitionPlan.size(); i ++) { |
| // iterate through every unassigned region |
| PartitionGroup pg = partitionPlan.get(i); |
| int totalNodes = pg.getTotalNodes(); |
| if(totalNodes < smallest) { |
| smallest = totalNodes; |
| index = i; |
| }// if partition group |
| }// for all partitionGroups |
| return index; |
| } |
| |
| |
| |
| |
| /** |
| * Sorted Region[] large to small |
| * @param regionSet |
| * @return |
| */ |
| @SuppressWarnings("nls") |
| public Region[] getSortedRegions(Set<String> regionSet) { |
| Region[] sortedRegions = new Region[regionSet.size()]; |
| int icount = 0; |
| for(String region: regionSet) { |
| Region r = worldSubCountryMap.get(region); |
| if(r == null) |
| System.err.println("Unable to find region "+region); |
| sortedRegions[icount] = r; |
| icount ++; |
| } |
| Arrays.sort(sortedRegions); |
| return sortedRegions; |
| } |
| |
| /** |
| * Sorted Country[] large to small |
| * @param countryList |
| * @return |
| */ |
| public Country[] getSortedCountries(List<Country> countryList) { |
| Country[] sortedCountries = countryList.toArray(new Country[countryList.size()]); |
| Arrays.sort(sortedCountries); |
| return sortedCountries; |
| } |
| |
| |
| /** |
| * Sorted String[] of county names (alphabetical) large to small |
| * @param countryList |
| * @return |
| */ |
| public String[] getSortedCountryNames(List<Country> countryList) { |
| String[] sortedCountries = new String[countryList.size()]; |
| int icount = 0; |
| for(Country ctry: countryList) { |
| sortedCountries[icount] = ctry.threeLetterCode; |
| icount ++; |
| } |
| Arrays.sort(sortedCountries); |
| return sortedCountries; |
| } |
| |
| |
| /** |
| * @param stemid |
| * @return administration level |
| */ |
| public int getAdminLevel(String stemid) { |
| |
| String [] splitID = stemid.split("-"); //$NON-NLS-1$ |
| if(splitID.length == 4) { |
| return 3; |
| } |
| else if(splitID.length == 3) { |
| return 2; |
| } |
| else if(splitID.length == 2) { |
| return 1; |
| } |
| return splitID.length-1; |
| } |
| |
| /** |
| * Gets a compatible input stream for the given URI |
| * @return |
| * @throws IOException |
| */ |
| private InputStream getInputStreamForURI(URI uri) throws IOException |
| { |
| return new ExtensibleURIConverterImpl().createInputStream(uri); |
| } |
| |
| /** |
| * read in a list of all countries |
| */ |
| @SuppressWarnings("nls") |
| public void mapCodes() { |
| |
| String record; |
| int recCount = 0; |
| BufferedReader d = null; |
| try { |
| d = new BufferedReader(new InputStreamReader(getInputStreamForURI(CODES_FILE_URI))); |
| |
| while ( (record=d.readLine()) != null ) { |
| recCount++; |
| StringTokenizer st = new StringTokenizer(record ); |
| |
| @SuppressWarnings("unused") |
| String name = st.nextToken(","); |
| |
| String twoLetter = st.nextToken(","); |
| String threeLetter = st.nextToken(","); |
| |
| regionCode2to3.put(twoLetter,threeLetter); |
| regionCode3to2.put(threeLetter,twoLetter); |
| } // lines |
| |
| } catch (IOException e) { |
| // catch io errors from FileInputStream or readLine() |
| Activator.logInformation(" IOException error!" + e.getMessage()); |
| } finally { |
| try { |
| d.close(); |
| } catch (Exception e) { } |
| } |
| } // read codes |
| |
| /** |
| * read in a map of countries and their sizes |
| */ |
| @SuppressWarnings("nls") |
| public void mapCountryStats() { |
| int index = 0; |
| String record; |
| int recCount = 0; |
| |
| BufferedReader d = null; |
| |
| try { |
| d = new BufferedReader(new InputStreamReader(getInputStreamForURI(STATS_FILE_0_URI))); |
| |
| while ( (record=d.readLine()) != null ) { |
| recCount++; |
| StringTokenizer st = new StringTokenizer(record,","); |
| |
| String threeLetter = st.nextToken(); |
| int numNodes = (new Integer(st.nextToken().trim())).intValue(); |
| |
| |
| String twoLetter = regionCode3to2.get(threeLetter); |
| Country country = new Country(threeLetter, twoLetter, index, numNodes); |
| |
| worldCountryList.add(index,country); |
| worldCountryMap.put(threeLetter, country); |
| index++; |
| |
| |
| } // lines |
| |
| level0CommonBorderMatrix = new int[worldCountryList.size()][worldCountryList.size()]; |
| Activator.logInformation("read "+index+" countries and their sizes"); |
| |
| |
| } catch (IOException e) { |
| // catch io errors from FileInputStream or readLine() |
| Activator.logInformation(" IOException error!" + e.getMessage()); |
| } finally { |
| try { |
| d.close(); |
| } catch (Exception e) { }; |
| } |
| } // map region stats matrix |
| |
| |
| /** |
| * read in a matrix of all region neighbor statistics |
| */ |
| @SuppressWarnings("nls") |
| public void mapSubCountryStats() { |
| int levelOneCount = 0; |
| String record; |
| int recCount = 0; |
| |
| BufferedReader d = null; |
| |
| // get the dimension of the Matrix from the first line of data |
| |
| try { |
| d = new BufferedReader(new InputStreamReader(getInputStreamForURI(STATS_FILE_1_URI))); |
| |
| while ( (record=d.readLine()) != null ) { |
| recCount++; |
| StringTokenizer st = new StringTokenizer(record,","); |
| |
| String levelOneID = st.nextToken(); |
| |
| int numNodes = (new Integer(st.nextToken().trim())).intValue(); |
| if(level1CommonBorderMatrix == null) { |
| NUM_LEVEL_1_IDS = st.countTokens(); |
| level1CommonBorderMatrix = new int[NUM_LEVEL_1_IDS][NUM_LEVEL_1_IDS]; |
| } |
| for (int i = 0; i < NUM_LEVEL_1_IDS; i ++) { |
| int val = (new Integer(st.nextToken().trim())).intValue(); |
| level1CommonBorderMatrix[levelOneCount][i] = val; |
| } |
| |
| String level0Code = ""; |
| int lvl = getAdminLevel(levelOneID); |
| if(lvl==0) { |
| level0Code = levelOneID; |
| } else { |
| String twoLetter = levelOneID.substring(0,2); |
| level0Code = regionCode2to3.get(twoLetter); |
| } |
| |
| Country ctry = worldCountryMap.get(level0Code); |
| |
| // never happens if data ok |
| if(ctry==null) { |
| Activator.logInformation("Error, ctry = null for level0Code= "+level0Code); |
| System.exit(1); |
| } |
| |
| Region region = new Region(levelOneID, levelOneCount, numNodes,ctry.threeLetterCode); |
| |
| worldStateList.add(levelOneCount,region); |
| worldSubCountryMap.put(levelOneID, region); |
| |
| levelOneIDList.add(levelOneID); |
| levelOneCount++; |
| |
| |
| } // lines |
| |
| Activator.logInformation("read matrix "+levelOneCount+" x "+NUM_LEVEL_1_IDS); |
| if(levelOneCount != NUM_LEVEL_1_IDS) { |
| Activator.logInformation("FAIL "+levelOneCount+" != "+NUM_LEVEL_1_IDS); |
| System.exit(1); |
| } |
| |
| } catch (IOException e) { |
| // catch io errors from FileInputStream or readLine() |
| Activator.logInformation(" IOException error!" + e.getMessage()); |
| } finally { |
| try { |
| d.close(); |
| } catch (Exception e) { }; |
| } |
| } // map region stats matrix |
| |
| /** |
| * This method is for testing only |
| * to validate the subCountry STATS matrix |
| */ |
| @SuppressWarnings("nls") |
| public void checkSumCountryStats() { |
| |
| String[] world = getSortedCountryNames(worldCountryList); |
| for(int i = 0; i < world.length; i ++) { |
| for(int j = 0; j < world.length; j ++) { |
| level0CommonBorderMatrix[i][j]=0; |
| } |
| } |
| |
| for(int i = 0; i < worldStateList.size(); i ++) { |
| int ii = getCountryIndex(worldStateList.get(i).parentCountryCode,world); |
| for(int j = 0; j < worldStateList.size(); j ++) { |
| int jj = getCountryIndex(worldStateList.get(j).parentCountryCode,world); |
| if(ii!=jj) level0CommonBorderMatrix[ii][jj]+= level1CommonBorderMatrix[i][j]; // don't report the internal country edges |
| } |
| } |
| Activator.logInformation(""); |
| Activator.logInformation("CheckSum for Stats Martix - compressed by country"); |
| Activator.logInformation(""); |
| for (int i = 0; i < world.length; i ++) { |
| String s = world[i]+" "; |
| for (int j = 0; j < 25; j ++) { |
| if(level0CommonBorderMatrix[i][j]<10) s += " "; |
| if(level0CommonBorderMatrix[i][j]<100) s += " "; |
| s += level0CommonBorderMatrix[i][j]+" "; |
| } |
| Activator.logInformation(s); |
| } |
| Activator.logInformation(""); |
| Activator.logInformation("CheckSum for Stats Martix - compressed by country"); |
| Activator.logInformation(""); |
| System.exit(0); |
| } |
| |
| /** |
| * |
| * @param threeLetterCode |
| * @param world |
| * @return |
| */ |
| @SuppressWarnings("nls") |
| public static int getCountryIndex(String threeLetterCode, String[] world) { |
| for(int i = 0; i < world.length; i ++) { |
| if(world[i].equalsIgnoreCase(threeLetterCode)) return i; |
| } |
| Activator.logInformation("Error mapping country index"); |
| System.exit(1); |
| return -1; // should never happen so |
| } |
| |
| /** |
| * inner class |
| * @author jhkauf |
| * |
| */ |
| public class Country implements Comparable<Country> { |
| |
| String threeLetterCode; |
| String twoLetterCode; |
| int index; |
| int numNodes; |
| |
| /** |
| * |
| * @param threeLetterCode |
| * @param twoLetterCode |
| * @param index |
| */ |
| public Country(String threeLetterCode, String twoLetterCode, int index, int numNodes) { |
| this.threeLetterCode = threeLetterCode; |
| this.twoLetterCode = twoLetterCode; |
| this.index = index; |
| this.numNodes = numNodes; |
| } |
| |
| |
| |
| /** |
| * compares two countries for sort |
| * @param other country |
| */ |
| public int compareTo(Country other) { |
| |
| // exception - large country with ZERO neighbors |
| // TODO check the STEM data here |
| // we many note be using all these PNG nodes |
| // they have no neighbors |
| if(this.threeLetterCode.equalsIgnoreCase("PNG")) return 1; //$NON-NLS-1$ |
| |
| if(this.numNodes > other.numNodes) { |
| return -1; |
| } else if(this.numNodes < other.numNodes) { |
| return 1; |
| } else return 0; |
| } |
| |
| } |
| |
| |
| /** |
| * inner class |
| * @author jhkauf |
| * |
| */ |
| public class Region implements Comparable<Region> { |
| |
| String regionID; |
| int index; |
| int numNodes; |
| String parentCountryCode; |
| |
| |
| /** |
| * @param regionID |
| * @param index |
| * @param numNodes |
| * @param parentCountry |
| */ |
| public Region(String regionID, int index, int numNodes, String parentCountry) { |
| this.regionID = regionID; |
| this.index = index; |
| this.numNodes = numNodes; |
| this.parentCountryCode = parentCountry; |
| } |
| |
| |
| |
| /** |
| * compares two countries for sort |
| * @param other region |
| */ |
| public int compareTo(Region other) { |
| |
| // exception - large region with ZERO neighbors |
| // TODO check the STEM data here |
| // we many note be using all these PNG nodes |
| // they have no neighbors |
| if(this.regionID.equalsIgnoreCase("PNG")) return 1; //$NON-NLS-1$ |
| |
| if(this.numNodes > other.numNodes) { |
| return -1; |
| } else if(this.numNodes < other.numNodes) { |
| return 1; |
| } else return 0; |
| } |
| |
| } |
| |
| /** |
| * inner class |
| * @author jhkauf |
| * |
| */ |
| public class PartitionGroup implements Comparable<PartitionGroup> { |
| |
| Set<Region> regionGroup = new HashSet<Region>(); |
| |
| Region nextCandidateMember = null; |
| |
| |
| /** |
| * |
| */ |
| public PartitionGroup() { |
| } |
| |
| /** |
| * returns the group as set of three letter region codes |
| * @return |
| */ |
| Set<String> getRegionCodes() { |
| Set<String> planSet = new HashSet<String>(); |
| for(Region region: regionGroup) { |
| planSet.add(region.regionID); |
| } |
| return planSet; |
| } |
| |
| /** |
| * |
| * @return |
| */ |
| public int getTotalNodes() { |
| int sum = 0; |
| for(Region r:regionGroup) { |
| sum += r.numNodes; |
| } |
| return sum; |
| }// getTotalNodes |
| |
| /** |
| * |
| * @param region |
| */ |
| public void add(Region region) { |
| regionGroup.add(region); |
| } |
| |
| /** |
| * |
| * @param other |
| * @return |
| */ |
| public int getCommonBorderArea(Region other) { |
| int surfaceArea = 0; |
| for(Region region: regionGroup) { |
| surfaceArea += level1CommonBorderMatrix[region.index][other.index]; |
| } |
| return surfaceArea; |
| } |
| |
| |
| |
| |
| |
| /** |
| * |
| * Count the unresolved or external edges in a regionGroup |
| * @return |
| */ |
| public int countUnresolvedBorderEdges() { |
| int surfaceArea = 0; |
| // for every region in this group |
| for(Region region: regionGroup) { |
| |
| // and for every possible neighbor region |
| for(int j = 0; j < NUM_LEVEL_1_IDS; j ++) { |
| // not diagonal element |
| if(j != region.index) { |
| Region target = worldStateList.get(j); |
| // IF region j is NOT in this group |
| if(!regionGroup.contains(target)) { |
| surfaceArea += level1CommonBorderMatrix[j][region.index]; |
| }// if target is remote |
| }// not diagonal (is target is OTHER region) |
| |
| }// all other target countries |
| |
| }// for all countries in this group |
| return surfaceArea; |
| }//countUnresolvedBorderEdges() |
| |
| |
| |
| |
| |
| |
| /** |
| * |
| * @param candidate |
| * @return |
| */ |
| public int getSurfaceArea(Region candidate) { |
| int surfaceArea = 0; |
| // for each region in THIS group |
| for(Region region: regionGroup) { |
| surfaceArea += level1CommonBorderMatrix[candidate.index][region.index]; |
| } |
| return surfaceArea; |
| }//getSurfaceArea |
| |
| |
| /** |
| * compares two countries for sort |
| * @param other region |
| */ |
| public int compareTo(PartitionGroup other) { |
| |
| int thisSize = this.getTotalNodes(); |
| int otherSize = other.getTotalNodes(); |
| if(thisSize < otherSize) { |
| return 1; |
| } else if(thisSize > otherSize) { |
| return -1; |
| } else return 0; |
| }//compareTo |
| |
| |
| |
| |
| |
| |
| }// PartitionGroup |
| |
| /** |
| * |
| * @param masterList |
| * @return |
| */ |
| public static int[][] getPartitionGroupBorderMatrix(List<PartitionGroup> masterList) { |
| int numGroups = masterList.size(); |
| int[][] matrix = new int[numGroups][numGroups]; |
| |
| for(int i = 0; i < numGroups; i ++) { |
| PartitionGroup pgi = masterList.get(i); |
| for(int j = 0; j < numGroups; j ++) { |
| matrix[i][j] = 0; |
| if(i!=j) { |
| PartitionGroup pgj = masterList.get(j); |
| for(Region region: pgj.regionGroup) { |
| matrix[i][j] += pgi.getCommonBorderArea(region); |
| } |
| } |
| |
| }// for j |
| } // for i |
| return matrix; |
| } |
| |
| |
| }// CountryGraphPatitionPlanner |