package i5.las2peer.services.ocd.metrics; import i5.las2peer.services.ocd.graphs.Cover; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import y.base.Node; import y.base.NodeCursor; public class OmegaIndex implements KnowledgeDrivenMeasure { @Override public void setParameters(Map<String, String> parameters) { } @Override public Map<String, String> getParameters() { return new HashMap<String, String>(); } @Override public double measure(Cover cover, Cover groundTruth) throws OcdMetricException, InterruptedException { Map<Set<Node>, Integer> sharedCommunitiesAlgo = getSharedCommunities(cover); Map<Set<Node>, Integer> sharedCommunitiesTruth = getSharedCommunities(groundTruth); int pairsInAgreementCount = 0; Map<Integer, Integer> sharedCommunityCountsAlgo = new HashMap<Integer, Integer>(); Map<Integer, Integer> sharedCommunityCountsTruth = new HashMap<Integer, Integer>(); NodeCursor nodesA = cover.getGraph().nodes(); NodeCursor nodesB = cover.getGraph().nodes(); /* * Calculates the number of nodes in agreement and of pairs sharing a given number of communities. */ while(nodesA.ok()) { Node nodeA = nodesA.node(); nodesB.toFirst(); while(nodesB.ok()) { if(Thread.interrupted()) { throw new InterruptedException(); } Node nodeB = nodesB.node(); if(nodeB.index() < nodeA.index()) { Set<Node> pair = new HashSet<Node>(); pair.add(nodeA); pair.add(nodeB); Integer sharedAlgo = sharedCommunitiesAlgo.get(pair); if(sharedAlgo == null) { sharedAlgo = 0; } Integer count = sharedCommunityCountsAlgo.get(sharedAlgo); if(count == null) { count = 1; } else { count++; } sharedCommunityCountsAlgo.put(sharedAlgo, count); Integer sharedTruth = sharedCommunitiesTruth.get(pair); if(sharedTruth == null) { sharedTruth = 0; } count = sharedCommunityCountsTruth.get(sharedTruth); if(count == null) { count = 1; } else { count++; } sharedCommunityCountsTruth.put(sharedTruth, count); if(sharedTruth == sharedAlgo) { pairsInAgreementCount++; } } else { break; } nodesB.next(); } nodesA.next(); } /* * Calculates the actual omega index. */ int n = cover.getGraph().nodeCount(); int pairsCount = ( n * (n - 1) ) / 2; double unadjustedIndex = 1d / (double) pairsCount * (double) pairsInAgreementCount; double expectedIndex = calculateExpectedIndex(sharedCommunityCountsAlgo, sharedCommunityCountsTruth, pairsCount); double metricValue = (unadjustedIndex - expectedIndex) / (1 - expectedIndex); return metricValue; } /* * Calculates the number of shared communities for each node pair. * @param cover The corresponding cover. * @return A mapping from the node pairs to the count of shared communities. */ private Map<Set<Node>, Integer> getSharedCommunities(Cover cover) throws InterruptedException { Map<Set<Node>, Integer> sharedCommunityCounts = new HashMap<Set<Node>, Integer>(); Integer count; Node nodeA; Node nodeB; NodeCursor nodesA = cover.getGraph().nodes(); NodeCursor nodesB = cover.getGraph().nodes(); for(int i = 0; i<cover.communityCount(); i++) { while(nodesA.ok()) { nodeA = nodesA.node(); if(cover.getBelongingFactor(nodeA, i) > 0) { while(nodesB.ok()) { if(Thread.interrupted()) { throw new InterruptedException(); } nodeB = nodesB.node(); /* * Pairs are regarded only once. */ if(nodeA.index() <= nodeB.index()) { break; } if(cover.getBelongingFactor(nodeB, i) > 0) { Set<Node> pair = new HashSet<Node>(); pair.add(nodeA); pair.add(nodeB); count = sharedCommunityCounts.get(pair); if(count == null) { count = 1; } else { count++; } sharedCommunityCounts.put(pair, count); } nodesB.next(); } } nodesB.toFirst(); nodesA.next(); } nodesA.toFirst(); } return sharedCommunityCounts; } private double calculateExpectedIndex(Map<Integer, Integer> sharedCommunityCountsAlgo, Map<Integer, Integer> sharedCommunityCountsTruth, int pairsCount) { double expectedIndex = 0; for(Map.Entry<Integer, Integer> entry : sharedCommunityCountsAlgo.entrySet()) { Integer truthValue = sharedCommunityCountsTruth.get(entry.getKey()); if(truthValue != null) { expectedIndex += truthValue * entry.getValue(); } } expectedIndex /= Math.pow(pairsCount, 2); return expectedIndex; } }