/** * This class computes some invariants about Agents to compare them. */ package com.plectix.simulator.util.string; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import com.plectix.simulator.staticanalysis.Agent; import com.plectix.simulator.staticanalysis.LinkStatus; import com.plectix.simulator.staticanalysis.Site; import com.plectix.simulator.util.PrimeNumbers; public final class AgentInvariant { public static final AgentInvariantComparator AGENT_INVARIANT_COMPARATOR = new AgentInvariantComparator(); public static final AgentInvariantRankComparator AGENT_INVARIANT_RANK_COMPARATOR = new AgentInvariantRankComparator(); public static final AgentInvariantNeighborSiteComparator AGENT_INVARIANT_NEIGHBOR_SITE_COMPARATOR = new AgentInvariantNeighborSiteComparator(); private int rankOld = 0; // 0 means not set yet private int rankNew = 0; // 0 means not set yet private int rankTemp = 0; // 0 means not set yet private long productOfNeighborPrimes = 0; private int numberOfConnections = 0; private int numberOfWildcards = 0; private Agent agent = null; // this is set in the constructor private List<Site> sortedSites = null; // we don't initialize this list if we don't have to... private List<AgentInvariant> neighborAgentList = null; // we don't initialize this list if we don't have to... public AgentInvariant(Agent agent) { if (agent == null) { throw new RuntimeException("Agent can not be null!"); } this.agent = agent; for (Site site : agent.getSites()) { if (site.getLinkState().getStatusLink() == LinkStatus.BOUND) { numberOfConnections++; } else if (site.getLinkState().getStatusLink() == LinkStatus.WILDCARD) { numberOfWildcards++; } else if (site.getLinkState().getStatusLink() != LinkStatus.FREE) { // we expect that the site will be either BOUND, WILDCARD, or FREE. throw exception otherwise: throw new RuntimeException("Unexpected State: Link state is neither BOUND nor WILDCARD nor FREE."); } } } /** * Returns the sites of this Agent sorted using {@link SiteComparator} * * @return */ public final List<Site> getSortedSites() { if (sortedSites == null) { sortedSites = new ArrayList<Site>(agent.getSites()); // here they will be sorted by name since they are on the same Agent: Collections.sort(sortedSites, SiteComparator.SITE_COMPARATOR); } return sortedSites; } /** * Compile a list of AgentInvariants which are bound to this AgentInvariant * * @param targetAgentAgentInvariantMap */ public final void computeNeighbors(Map<Agent, AgentInvariant> targetAgentAgentInvariantMap) { if (neighborAgentList != null) { return; } neighborAgentList = new ArrayList<AgentInvariant>(); for (Site site : getSortedSites()) { if (site.getLinkState().getStatusLink() == LinkStatus.BOUND) { Site connectedSite = site.getLinkState().getConnectedSite(); if (connectedSite != null) { AgentInvariant neighbor = targetAgentAgentInvariantMap.get(connectedSite.getParentAgent()); if (neighbor == null) { throw new RuntimeException("Could not find neighbor Agent in map!"); } // all the neighbors are sorted in the alphabetical order of the sites they are connected to neighborAgentList.add(neighbor); } } } } /** * Computes the product of prime numbers corresponding to neighbor Agents' ranks. */ public final void computeProductOfNeighborPrimes() { if (neighborAgentList == null) { throw new RuntimeException("neighborAgentList is not initialized yet!"); } productOfNeighborPrimes = 1; for (AgentInvariant agentInvariant : neighborAgentList) { productOfNeighborPrimes *= PrimeNumbers.FIRST_8242[agentInvariant.getRankNew() + agentInvariant.getNeighborRank(this) - 1]; } } /** * Returns the index of this neighbor in the neighbor list * * @param neighborAgent * @return */ private final int getNeighborRank(AgentInvariant neighborAgent) { final int index = neighborAgentList.indexOf(neighborAgent); if (index == -1) { throw new RuntimeException("Given agent is not a neighbor!"); } return index; } /** * Copies the new Rank to the old Rank. */ public final void saveRank() { rankOld = rankNew; } /** * Checks whether the new Rank is equal to the old Rank or not * * @return */ public final boolean areRanksEqual() { return rankNew == rankOld; } /** * Multiplies the new Rank by 2 */ public final void doubleRankNew() { rankNew *= 2; } /** * Returns the number of sites of this Agent. * * @return */ final int getNumberOfSites() { return agent.getSites().size(); } /** * Returns the name of this Agent. * * @return */ final String getName() { return agent.getName(); } /** * Returns the Agent in this AgentInvariant. * * @return */ public final Agent getAgent() { return agent; } /** * Returns the product of neighbor primes computed with {@link #computeProductOfNeighborPrimes()} * * @return */ public final long getProductOfNeighborPrimes() { return productOfNeighborPrimes; } /** * Returns the new Rank of this Agent * * @return */ public final int getRankNew() { return rankNew; } /** * Sets the new Rank of this Agent. * * @param rank */ public final void setRankNew(int rank) { rankNew = rank; } /** * Returns the temporary Rank of this Agent * * @return */ public final int getRankTemp() { return rankTemp; } /** * Sets the temporary Rank of this Agent. * * @param rank */ public final void setRankTemp(int rank) { rankTemp = rank; } /** * Returns the number of connections of this Agent. * * @return */ public final int getNumberOfConnections() { return numberOfConnections; } /** * Returns the number of wildcard sites of this Agent. * * @return */ final int getNumberOfWildcards() { return numberOfWildcards; } private static final Site getConnectionSite(AgentInvariant thisAgent, AgentInvariant neighborAgent) { for (Site site : thisAgent.getAgent().getSites()) { if (site.getLinkState().getStatusLink() == LinkStatus.BOUND) { Site connectedSite = site.getLinkState().getConnectedSite(); if (connectedSite == null) { return null; } if (connectedSite.getParentAgent() == neighborAgent.getAgent()) { return site; } } } throw new RuntimeException("Agents are not neighbors!"); } /** * This class compares two AgentInvariants with respect to some "graph theoretical invariants". * The invariants selected here is not a "complete" set. * No "perfect" set of invariants is known that will distinguish all possible graph symmetries. * We can add more invariants or eliminate some to fine tune the algorithm's performance. * * <br><br> * Here is the comparison rules: * * <ul> * <li> The Agent with less connections comes before * <li> If two Agents have the same number of connections, then the Agent with less sites comes before * <li> If two Agents are still equivalent, then the Agents are sorted according to their names * <li> If two Agents are still equivalent, we compare their Sites using {@link SiteComparator} * </ul> * * @author ecemis */ public static final class AgentInvariantComparator implements Comparator<AgentInvariant> { private AgentInvariantComparator() { super(); } public final int compare(final AgentInvariant o1, final AgentInvariant o2) { if (o1 == null) { if (o2 == null) { return 0; } else { return -1; } } else { if (o2 == null) { return 1; } else { // don't handle this case here... go below... } } // first compare the number of connections int result = Double.compare(o1.getNumberOfConnections(), o2.getNumberOfConnections()); if (result != 0) { return result; } result = Double.compare(o1.getNumberOfWildcards(), o2.getNumberOfWildcards()); if (result != 0) { return result; } // then compare the number of sites result = Double.compare(o1.getNumberOfSites(), o2.getNumberOfSites()); if (result != 0) { return result; } // then compare agent names: result = o1.getName().compareTo(o2.getName()); if (result != 0) { return result; } // agent names are the same... and the number of connections are also the same... // so let's compare sites: List<Site> sortedSites1 = o1.getSortedSites(); List<Site> sortedSites2 = o2.getSortedSites(); for (int i= 0; i < sortedSites1.size(); i++) { result = SiteComparator.SITE_COMPARATOR.compare(sortedSites1.get(i), sortedSites2.get(i)); if (result != 0) { return result; } } // all the invariants are the same! return 0; } } /** * This class compares two AgentInvariants with respect * to their Ranks and to their product of neighbor primes. * * <br><br> * Here is the comparison rules: * * <ul> * <li> The Agent with lowest rank comes before * <li> If two Agents have the same rank, then the Agent with the lowest product of primes comes before * </ul> * * @author ecemis * */ public static final class AgentInvariantRankComparator implements Comparator<AgentInvariant> { private AgentInvariantRankComparator() { super(); } public final int compare(AgentInvariant o1, AgentInvariant o2) { int result = Double.compare(o1.getRankNew(), o2.getRankNew()); if (result != 0) { return result; } return Double.compare(o1.getProductOfNeighborPrimes(), o2.getProductOfNeighborPrimes()); } } public static final class AgentInvariantNeighborSiteComparator implements Comparator<AgentInvariant> { private AgentInvariantNeighborSiteComparator() { super(); } public final int compare(final AgentInvariant o1, final AgentInvariant o2) { if (o1 == null) { if (o2 == null) { return 0; } else { return -1; } } else { if (o2 == null) { return 1; } else { // don't handle this case here... go below... } } int result = Double.compare(o1.neighborAgentList.size(), o2.neighborAgentList.size()); if (result != 0) { // this case should never happen! but it doesn't hurt to have it here... return result; } List<AgentInvariant> neighborAgentList1 = new ArrayList<AgentInvariant>(o1.neighborAgentList); List<AgentInvariant> neighborAgentList2 = new ArrayList<AgentInvariant>(o2.neighborAgentList); Collections.sort(neighborAgentList1, AGENT_INVARIANT_RANK_COMPARATOR); Collections.sort(neighborAgentList2, AGENT_INVARIANT_RANK_COMPARATOR); for (int i= 0; i < neighborAgentList1.size(); i++) { if (i+1 != neighborAgentList1.size()) { final boolean neighborAgentList1IsRandom = AGENT_INVARIANT_RANK_COMPARATOR.compare(neighborAgentList1.get(i), neighborAgentList1.get(i+1)) == 0; final boolean neighborAgentList2IsRandom = AGENT_INVARIANT_RANK_COMPARATOR.compare(neighborAgentList2.get(i), neighborAgentList2.get(i+1)) == 0; if (neighborAgentList1IsRandom) { if (neighborAgentList2IsRandom) { // can not compare randomly! return 0; } else { return +1; } } else { if (neighborAgentList2IsRandom) { return -1; } else { // compare below... } } } result = AGENT_INVARIANT_RANK_COMPARATOR.compare(neighborAgentList1.get(i), neighborAgentList2.get(i)); if (result != 0) { return result; } if (neighborAgentList1.get(i).getRankNew() >= o1.getRankNew()) { return 0; } // Let's find the sites connected to that neighbor: Site site1 = getConnectionSite(o1, neighborAgentList1.get(i)); Site site2 = getConnectionSite(o2, neighborAgentList2.get(i)); // some part of the following comparison is redundant. but at least we don't duplicate any code! result = SiteComparator.SITE_COMPARATOR.compare(site1, site2); if (result != 0) { return result; } } // could not differentiate ;-( return 0; } } }