package com.plectix.simulator.staticanalysis; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.plectix.simulator.interfaces.ConnectedComponentInterface; import com.plectix.simulator.simulationclasses.injections.Injection; import com.plectix.simulator.simulationclasses.injections.LiftElement; import com.plectix.simulator.simulationclasses.probability.SkipListSelector; import com.plectix.simulator.simulationclasses.probability.WeightedItemSelector; import com.plectix.simulator.simulationclasses.solution.SuperSubstance; import com.plectix.simulator.simulator.ThreadLocalData; import com.plectix.simulator.util.string.ConnectedComponentToSmilesString; /** * This class implements "connected component" entity.<br><br> * It unites a pack of connected agents. We state that all the agents included in * connected component somehow connected with each other (there is a path consisted by * several connections, connecting each pair of agents in fixed connected component).<br><br> * * @author avokhmin * */ public class ConnectedComponent implements ConnectedComponentInterface { private final List<Agent> agents = new ArrayList<Agent>(); private Map<String, List<SpanningTree>> spanningTreeMap; // TODO please rename it, or even rename it private List<Agent> agentFromSolutionForRightHandSide; private Map<Integer, Agent> agentsLinks; private List<Site> injectedSites; private Rule rule; private SuperSubstance superSubstance = null; // for the better searching private WeightedItemSelector<Injection> injections = new SkipListSelector<Injection>(); private final boolean isEmpty; /** * private empty connected component constructor */ public ConnectedComponent() { isEmpty = true; agents.add(new Agent()); injections = new WeightedItemSelector<Injection>() { private final Injection injection = ThreadLocalData.getEmptyInjection(); @Override public Set<Injection> asSet() { Set<Injection> oneItemSet = new LinkedHashSet<Injection>(); oneItemSet.add(injection); return oneItemSet; } @Override public double getTotalWeight() { return injection.getWeight(); } @Override public Injection select() { return injection; } @Override public void updatedItem(Injection item) { } @Override public void updatedItems( Collection<Injection> changedWeightedItemList) { } }; agentFromSolutionForRightHandSide = new ArrayList<Agent>(); } /** * Main constructor. Creates connected component with given list of connected agents. * @param connectedAgents list of agents, which can be source for the new connected component */ public ConnectedComponent(Collection<Agent> connectedAgents) { agents.addAll(connectedAgents); agentFromSolutionForRightHandSide = new ArrayList<Agent>(); isEmpty = false; } public final boolean isEmpty() { return isEmpty; } public final void setSuperSubstance(SuperSubstance substance) { superSubstance = substance; } public final SuperSubstance getSubstance() { return superSubstance; } /** * This method registers some agents from solution when apply rule to the util collection needed * on positive update. * @param agentFromSolutionForRHS agent to be registered */ //TODO move? public final void addAgentFromSolutionForRHS(Agent agent) { this.agentFromSolutionForRightHandSide.add(agent); } /** * This method clears util collection of solution agents needed * on positive update. */ //TODO move? public final void clearAgentsFromSolutionForRHS() { agentFromSolutionForRightHandSide.clear(); } /** * This method returns util collection of agents needed on positive update. * @return util list of agents, needed on positive update */ //TODO move? public final List<Agent> getAgentFromSolutionForRHS() { return agentFromSolutionForRightHandSide; } /** * This method registers injection to current connected component with given id. * @param injection injection to register * @param id id of injection */ private final void addInjection(Injection injection) { injections.updatedItem(injection); } /** * This method unregisters given injection from current connected component * @param injection injection to remove */ public final void removeInjection(Injection injection) { injection.eliminate(); } public long getInjectionsWeight() { return (long)(injections.getTotalWeight()); } /** * This method initializes "spanning tree map" structure, which is used for comparing two components * and so on */ public final void initSpanningTreeMap() { SpanningTree spTree; spanningTreeMap = new LinkedHashMap<String, List<SpanningTree>>(); if (agents.isEmpty()) return; for (Agent agentAdd : agents) { spTree = new SpanningTree(agents.size(), agentAdd); List<SpanningTree> list = spanningTreeMap .get(agentAdd.getName()); if (list == null) { list = new ArrayList<SpanningTree>(); spanningTreeMap.put(agentAdd.getName(), list); } list.add(spTree); } } /** * Sets new injection for this connected component. * @param injection injection to be set */ public final void setInjection(Injection injection) { this.addInjection(injection); for (Site changedSite : injectedSites) { changedSite.addToLift(new LiftElement(this, injection)); } } /** * Returns new injection for current ConnectedComponent by input agent.<br> * If injection can't be create, returns "null". * @param agent given agent */ public final Injection createInjection(Agent agent) { if (unify(agent)) { Injection injection = new Injection(this, injectedSites, agentsLinks); return injection; } return null; } /** * This method does positive update for current ConnectedComponent using given list of * connected components. * @param connectedComponentList list of ConnectedComponents to be used */ public final void doPositiveUpdate(List<ConnectedComponentInterface> connectedComponentList) { if (connectedComponentList == null) return; for (ConnectedComponentInterface cc : connectedComponentList) { for (Agent agent : cc.getAgentFromSolutionForRHS()) { Injection inj = this.createInjection(agent); if (inj != null) { if (!agent.hasSimilarInjection(inj)) { setInjection(inj); } } } } } /** * This method returns all agents from current ConnectedComponent * @return list of this component's agents */ public final List<Agent> getAgents() { return agents; } public final boolean unify(Agent agent) { injectedSites = new ArrayList<Site>(); agentsLinks = new LinkedHashMap<Integer, Agent>(); if (spanningTreeMap == null) return false; List<SpanningTree> spList = spanningTreeMap.get(agent.getName()); if (spList == null) { return false; } for (SpanningTree tree : spList) { injectedSites.clear(); agentsLinks.clear(); if (tree != null) { tree.resetNewVertex(); if (agents.get(tree.getRootIndex()).getSites().isEmpty()) { injectedSites.add(agent.getDefaultSite()); agentsLinks.put(0, agent); return true; } else { if (compareAgents(agents.get(tree.getRootIndex()), agent)) if (viewSpanningTree(agent, tree, tree.getRootIndex(), false)) if (this.getAgents().size() == agentsLinks.size() && isInjectionCorrect()) return true; else { injectedSites.clear(); agentsLinks.clear(); } } } } return false; } private boolean isInjectionCorrect(){ for(int i = 0; i < agents.size(); i++){ Agent agentThis = agents.get(i); Agent agentSolution = agentsLinks.get(agentThis.getIdInConnectedComponent()); for(Site siteThis : agentThis.getSites()){ Site connectedSiteThis = siteThis.getLinkState().getConnectedSite(); if(connectedSiteThis == null) continue; int linkAgentThisId = connectedSiteThis.getParentAgent().getIdInConnectedComponent(); Site connectedSiteSolution = agentSolution.getSiteByName(siteThis.getName()).getLinkState().getConnectedSite(); if(connectedSiteSolution.getParentAgent().getId() != agentsLinks.get(linkAgentThisId).getId()) return false; } } return true; } public final boolean isAutomorphicTo(Agent agent) { if (spanningTreeMap == null) return false; List<SpanningTree> spList = spanningTreeMap.get(agent.getName()); if (spList == null) return false; for (SpanningTree tree : spList) { if (tree != null) { tree.resetNewVertex(); if (agents.get(tree.getRootIndex()).getSites().isEmpty() && agent.getSites().isEmpty()) { return true; } else { if (agentsAreCompletelyEqual( agents.get(tree.getRootIndex()), agent)) if (viewSpanningTree(agent, tree, tree.getRootIndex(), true)) return true; } } } return false; } /** * This method compares two agents for being completely equal. * @param sourceAgent first agent * @param targetAgent second agent * @return <tt>true</tt> if these agents are completely equal, otherwise <tt>false</tt> */ //TODO MOVE?? private final boolean agentsAreCompletelyEqual(Agent sourceAgent, Agent targetAgent) { if (sourceAgent == null || targetAgent == null) return false; if (sourceAgent.getSites().size() != targetAgent.getSites().size()) return false; for (Site cc1Site : sourceAgent.getSites()) { Site cc2Site = targetAgent.getSiteByName(cc1Site.getName()); if (cc2Site == null) return false; if (!cc1Site.expandedEqualz(cc2Site, true)) return false; } return true; } /** * Util method for partial agents comparison * @param currentAgent first agent * @param solutionAgent second agent * @return <tt>true</tt> if these agents are partially equal, otherwise <tt>false</tt> */ private final boolean compareAgents(Agent currentAgent, Agent solutionAgent) { if (currentAgent == null || solutionAgent == null) return false; for (Site site : currentAgent.getSites()) { Site solutionSite = solutionAgent.getSiteByName(site.getName()); if (solutionSite == null) return false; if (!site.expandedEqualz(solutionSite, false)) return false; injectedSites.add(solutionSite); } agentsLinks.put(currentAgent.getIdInConnectedComponent(), solutionAgent); return true; } /** * Util method, uses in {@link #viewSpanningTree(Agent, SpanningTree, int, boolean)}. * @param sourceAgent given agent * @param targetAgent given agent * @return Returns list of connect sites between given agents */ private final List<Site> getConnectedSite(Agent sourceAgent, Agent targetAgent) { List<Site> siteList = new ArrayList<Site>(); for (Site sF : sourceAgent.getSites()) { for (Site sT : targetAgent.getSites()) { if (sF == sT.getLinkState().getConnectedSite()) { siteList.add(sF); } } } return siteList; } /** * Util method. */ private final boolean viewSpanningTree(Agent agent, SpanningTree spanningTree, int rootVertex, boolean doCompleteComparision) { spanningTree.setTrue(rootVertex); for (Integer v : spanningTree.getVertexes()[rootVertex]) { Agent cAgent = agents.get(v);// get next agent from spanning if (!(spanningTree .getNewVertexElement(cAgent.getIdInConnectedComponent()))) { List<Site> sitesFrom = getConnectedSite(agents .get(rootVertex), agents.get(v)); Agent sAgent = agent.findLinkAgent(cAgent, sitesFrom); if (doCompleteComparision && !(agentsAreCompletelyEqual(cAgent, sAgent))) return false; if (!doCompleteComparision && !compareAgents(cAgent, sAgent)) return false; viewSpanningTree(sAgent, spanningTree, v, doCompleteComparision); } } return true; } /** * This method sets rule containing this connected component. * @param rule new value */ public final void setRule(Rule rule) { this.rule = rule; } /** * This method returns all injections from current connected components. * @return list of injections from this connected component */ // TODO get rid of public final Collection<Injection> getInjectionsList() { return injections.asSet(); } public final void updateInjection(Injection injection, long newPower) { injection.setPower(newPower); injections.updatedItem(injection); } /** * This method returns random injection from current connected component * @param random random number generator * @return random injection from current connected component */ public final Injection getRandomInjection() { return injections.select(); } /** * This method returns first injection from current ConnectedComponent. * Used only for checking clashes in case of infinite rated rules * @return first injection from list of injections */ public final Injection getFirstInjection() { // TODO check if we really need it return injections.select(); } /** * This method sorts agents from current connected component by id in rule handside. * @return sorted collection of component's agents */ public final List<Agent> getAgentsSortedByIdInRule() { List<Agent> temp = new ArrayList<Agent>(); temp.addAll(agents); Collections.sort(temp); return temp; } private final Set<Injection> getIncomingInjectionsSet() { Set<Injection> injList = new LinkedHashSet<Injection>(); for (Agent agent : agents) { for (Site site : agent.getSites()) { for (LiftElement lift : site.getLift()) { injList.add(lift.getInjection()); } } // default-site case for (LiftElement lift : agent.getDefaultSite().getLift()) { injList.add(lift.getInjection()); } } return injList; } public final void burnIncomingInjections() { for (Injection inj : getIncomingInjectionsSet()) { inj.setSimple(); } } public final void incrementIncomingInjections() { for (Injection inj : getIncomingInjectionsSet()) { inj.incPower(); } } public final void deleteIncomingInjections() { for (Injection injection : getIncomingInjectionsSet()) { if (injection != ThreadLocalData.getEmptyInjection()) { for (Site site : injection.getChangedSites()) { site.getParentAgent().getDefaultSite().clearIncomingInjections(injection); site.getParentAgent().getDefaultSite().clearLifts(); site.clearIncomingInjections(injection); site.clearLifts(); } for (Site site : injection.getSiteList()) { site.removeInjectionFromLift(injection); } injection.getConnectedComponent().removeInjection(injection); } } } // -----------------------hash, toString, equals----------------------------- public final String getSmilesString() { return ConnectedComponentToSmilesString.getInstance().toUniqueString(this); } @Override public final String toString() { if (this.isEmpty()) { return "EMPTY"; } return getSmilesString(); } }