package edu.nd.nina.generate; import java.util.Collections; import java.util.Hashtable; import java.util.Map; import java.util.Vector; import edu.nd.nina.DirectedGraph; import edu.nd.nina.Graph; import edu.nd.nina.VertexFactory; import edu.nd.nina.math.Randoms; enum StopReason {srOK, srFlood, srTimeLimit}; /** * * @author Tim Weninger, based on Jure's Work */ public class ForestFireGraphGenerator<V, E> implements GraphGenerator<V, E, V> { //~ Instance fields -------------------------------------------------------- private Boolean BurnExpFire; // burn Exponential or Geometric fire private Integer StartNodes; // start a graph with N isolated nodes private Float FwdBurnProb, BckBurnProb, ProbDecay; // Forest Fire parameters private Float Take2AmbProb, OrphanProb; private int timeLimitSec, NNodes; private Boolean FloodStop; //~ Constructors ----------------------------------------------------------- public ForestFireGraphGenerator(boolean BurnExpFireP, int StartNNodes, Float ForwBurnProb, Float BackBurnProb, Float DecayProb, Float Take2AmbasPrb, Float OrphanPrb) { BurnExpFire = BurnExpFireP; StartNodes = StartNNodes; FwdBurnProb = ForwBurnProb; BckBurnProb = BackBurnProb; ProbDecay = DecayProb; Take2AmbProb = Take2AmbasPrb; OrphanProb = OrphanPrb; timeLimitSec = 30*60; NNodes = 0; FloodStop = true; } //~ Methods ---------------------------------------------------------------- /** * {@inheritDoc} */ public void generateGraph( Graph<V, E> target, VertexFactory<V> vertexFactory, Map<String, V> resultMap) { if(target instanceof DirectedGraph<?,?>){ DirectedGraph<V,E> g = (DirectedGraph<V,E>)target; addNodes(g, vertexFactory, NNodes, FloodStop); }else{ throw new IllegalArgumentException("Graph needs to be directed"); } } /** * * @param target * @param vertexFactory * @param GraphNodes * @param floodStop * @return */ private StopReason addNodes(DirectedGraph<V, E> target, VertexFactory<V> vertexFactory, int GraphNodes, Boolean floodStop) { System.out .printf("\n***ForestFire: %s Nodes:%d StartNodes:%d Take2AmbProb:%g\n", BurnExpFire ? "ExpFire" : "GeoFire", GraphNodes, StartNodes, Take2AmbProb); System.out .printf(" FwdBurnP:%g BckBurnP:%g ProbDecay:%g Orphan:%g\n", FwdBurnProb, BckBurnProb, ProbDecay, OrphanProb); Long ExeTm = System.currentTimeMillis(); int Burned1 = 0, Burned2 = 0, Burned3 = 0; // last 3 fire sizes // create initial set of nodes if (target.vertexSet().size() == 0) { for (int n = 0; n < StartNodes; n++) { target.addVertex(vertexFactory.createVertex()); } } int NEdges = target.edgeSet().size(); // forest fire Randoms r = new Randoms(0); ForestFire<V, E> FF = new ForestFire<V, E>(target, FwdBurnProb, BckBurnProb, ProbDecay, r); // add nodes for (int NNodes = target.vertexSet().size() + 1; NNodes <= GraphNodes; NNodes++) { V NewId = vertexFactory.createVertex(); target.addVertex(NewId); // not an Orphan (burn fire) if (OrphanProb == 0.0 || r.GetUniDev() > OrphanProb) { // infect ambassadors if (Take2AmbProb == 0.0 || r.GetUniDev() > Take2AmbProb || target.vertexSet().size() - 1 < 2) { FF.Infect(target.randomVertex(r)); // take 1 ambassador } else { final V AmbassadorNId1 = target.randomVertex(r); V AmbassadorNId2 = target.randomVertex(r); while (AmbassadorNId1 == AmbassadorNId2) { AmbassadorNId2 = target.randomVertex(r); } Vector<V> v = new Vector<V>(); v.add(AmbassadorNId1); v.add(AmbassadorNId2); FF.Infect(v); // take 2 ambassadors } // burn fire if (BurnExpFire) { FF.BurnExpFire(); } else { FF.BurnGeoFire(); } // add edges to burned nodes for (int e = 0; e < FF.GetBurned(); e++) { target.addEdge(NewId, FF.GetBurnedNId(e)); NEdges++; } Burned1 = Burned2; Burned2 = Burned3; Burned3 = FF.GetBurned(); } else { // Orphan (zero out-links) Burned1 = Burned2; Burned2 = Burned3; Burned3 = 0; } if (NNodes % 1000 == 0) { System.out.printf("(%d, %d) burned: [%d,%d,%d] [%s]\n", NNodes, NEdges, Burned1, Burned2, Burned3, System.currentTimeMillis() - ExeTm); } // average node degree is more than 500 if (FloodStop && NEdges > GraphNodes && (NEdges / (double) NNodes > 1000.0)) { System.out.printf(". FLOOD. G(%6d, %6d)\n", NNodes, NEdges); return StopReason.srFlood; } if (NNodes % 1000 == 0 && timeLimitSec > 0 && (System.currentTimeMillis() - ExeTm) / 1000d > timeLimitSec) { System.out.printf(". TIME LIMIT. G(%d, %d)\n", target .vertexSet().size(), target.edgeSet().size()); return StopReason.srTimeLimit; } } assert (target.edgeSet().size() == NEdges); return StopReason.srOK; } public void setNumNodes(int nNodes) { NNodes = nNodes; } public void setFloodStop(boolean b) { FloodStop = b; } public String getParamString() { return String .format("%s FWD:%g BCK:%g, StartNds:%d, Take2:%g, Orphan:%g, ProbDecay:%g", BurnExpFire ? "EXP" : "GEO", FwdBurnProb, BckBurnProb, StartNodes, Take2AmbProb, OrphanProb, ProbDecay); } } class ForestFire<V, E> { Randoms Rnd; DirectedGraph<V, E> Graph; Float FwdBurnProb, BckBurnProb, ProbDecay; // nodes to start fire Vector<V> InfectNIdV; // nodes that got burned (FF model creates edges to them) Vector<V> BurnedNIdV; // statistics // total burned, currently burning, newly burned in current time step Vector<Integer> NBurnedTmV, NBurningTmV, NewBurnedTmV; public ForestFire(DirectedGraph<V, E> target, Float fwdBurnProb, Float bckBurnProb, Float probDecay, Randoms r) { Graph = target; FwdBurnProb = fwdBurnProb; BckBurnProb = bckBurnProb; ProbDecay = probDecay; Rnd = r; NBurnedTmV = new Vector<Integer> (); NBurningTmV = new Vector<Integer> (); NewBurnedTmV = new Vector<Integer> (); } public int GetBurned() { return BurnedNIdV.size(); } public V GetBurnedNId(int n){ return BurnedNIdV.get(n); } public void BurnGeoFire() { final Float OldFwdBurnProb = FwdBurnProb; final Float OldBckBurnProb = BckBurnProb; final int NInfect = InfectNIdV.size(); // const TNGraph& G = *Graph; // burned nodes Hashtable<Integer, V> BurnedNIdH = new Hashtable<Integer, V>(); // currently burning nodes Vector<V> BurningNIdV = InfectNIdV; // nodes newly burned in current step Vector<V> NewBurnedNIdV = new Vector<V>(); // has unburned neighbors boolean HasAliveInNbrs = false, HasAliveOutNbrs = false; // NIds of alive neighbors Vector<V> AliveNIdV = new Vector<V>(); int NBurned = NInfect, time; for (int i = 0; i < InfectNIdV.size(); i++) { BurnedNIdH.put(i, InfectNIdV.get(i)); } NBurnedTmV.clear(); NBurningTmV.clear(); NewBurnedTmV.clear(); for (time = 0;; time++) { NewBurnedNIdV.clear(); // for each burning node for (int i = 0; i < BurningNIdV.size(); i++) { V Node = BurningNIdV.get(i); // find unburned links HasAliveOutNbrs = false; // unburned links AliveNIdV.clear(); // burn forward links (out-links) for (E e : Graph.outgoingEdgesOf(Node)) { V OutNId = Graph.getEdgeTarget(e); // not yet burned if (!BurnedNIdH.containsKey(OutNId)) { HasAliveOutNbrs = true; AliveNIdV.add(OutNId); } } // number of links to burn (geometric coin). Can also burn 0 // links final int BurnNFwdLinks = Rnd.GetGeoDev(1.0 - FwdBurnProb) - 1; if (HasAliveOutNbrs && BurnNFwdLinks > 0) { Collections.shuffle(AliveNIdV, Rnd.getRandom()); for (int j = 0; j < Math.min(BurnNFwdLinks, AliveNIdV.size()); j++) { BurnedNIdH.put(NBurned, AliveNIdV.get(j)); NewBurnedNIdV.add(AliveNIdV.get(j)); NBurned++; } } // backward links if (BckBurnProb > 0.0) { // find unburned links HasAliveInNbrs = false; AliveNIdV.clear(); for (E e : Graph.incomingEdgesOf(Node)) { V InNId = Graph.getEdgeSource(e); if (!BurnedNIdH.containsKey(InNId)) { // not yet burned HasAliveInNbrs = true; AliveNIdV.add(InNId); } } } // number of links to burn (geometric coin). Can also burn 0 // links final int BurnNBckLinks = Rnd.GetGeoDev(1.0 - BckBurnProb) - 1; if (HasAliveInNbrs && BurnNBckLinks > 0) { Collections.shuffle(AliveNIdV, Rnd.getRandom()); for (int j = 0; j < Math.min(BurnNBckLinks, AliveNIdV.size()); j++) { BurnedNIdH.put(NBurned, AliveNIdV.get(j)); NewBurnedNIdV.add(AliveNIdV.get(j)); NBurned++; } } } NBurnedTmV.add(NBurned); NBurningTmV.add(BurningNIdV.size()); NewBurnedTmV.add(NewBurnedNIdV.size()); // BurningNIdV.AddV(NewBurnedNIdV); // node is burning eternally //SWAP // node is burning just 1 time step Vector<V> t = BurningNIdV; BurningNIdV = NewBurnedNIdV; NewBurnedNIdV = t; if (BurningNIdV.isEmpty()) { break; } FwdBurnProb = FwdBurnProb * ProbDecay; BckBurnProb = BckBurnProb * ProbDecay; } BurnedNIdV = new Vector<V>(BurnedNIdH.size()); for (int i = 0; i < BurnedNIdH.size(); i++) { BurnedNIdV.add(BurnedNIdH.get(i)); } FwdBurnProb = OldFwdBurnProb; BckBurnProb = OldBckBurnProb; } // burn each link independently (forward with FwdBurnProb, backward with // BckBurnProb) public void BurnExpFire() { final Float OldFwdBurnProb = FwdBurnProb; final Float OldBckBurnProb = BckBurnProb; final int NInfect = InfectNIdV.size(); // const TNGraph& G = *Graph; // burned nodes Hashtable<Integer, V> BurnedNIdH = new Hashtable<Integer, V>(); // currently burning nodes Vector<V> BurningNIdV = InfectNIdV; // nodes newly burned in current step Vector<V> NewBurnedNIdV = new Vector<V>(); // has unburned neighbors boolean HasAliveNbrs; int NBurned = NInfect, NDiedFire = 0; for (int i = 0; i < InfectNIdV.size(); i++) { BurnedNIdH.put(i, InfectNIdV.get(i)); } NBurnedTmV.clear(); NBurningTmV.clear(); NewBurnedTmV.clear(); for (int time = 0;; time++) { NewBurnedNIdV.clear(); // for each burning node for (int i = 0; i < BurningNIdV.size(); i++) { V Node = BurningNIdV.get(i); HasAliveNbrs = false; NDiedFire = 0; // burn forward links (out-links) for (E e : Graph.outgoingEdgesOf(Node)) { V OutNId = Graph.getEdgeTarget(e); // not yet burned if (!BurnedNIdH.containsKey(OutNId)) { HasAliveNbrs = true; if (Rnd.GetUniDev() < FwdBurnProb) { BurnedNIdH.put(NBurned, OutNId); NewBurnedNIdV.add(OutNId); NBurned++; } } } // burn backward links (in-links) if (BckBurnProb > 0.0) { for (E e : Graph.incomingEdgesOf(Node)) { V InNId = Graph.getEdgeSource(e); if (!BurnedNIdH.containsKey(InNId)) { // not yet burned HasAliveNbrs = true; if (Rnd.GetUniDev() < BckBurnProb) { BurnedNIdH.put(NBurned, InNId); NewBurnedNIdV.add(InNId); NBurned++; } } } } if (!HasAliveNbrs) { NDiedFire++; } } NBurnedTmV.add(NBurned); NBurningTmV.add(BurningNIdV.size() - NDiedFire); NewBurnedTmV.add(NewBurnedNIdV.size()); // BurningNIdV.AddV(NewBurnedNIdV); // node is burning eternally //SWAP // node is burning just 1 time step Vector<V> t = BurningNIdV; BurningNIdV = NewBurnedNIdV; NewBurnedNIdV = t; if (BurningNIdV.isEmpty()) { break; } FwdBurnProb = FwdBurnProb * ProbDecay; BckBurnProb = BckBurnProb * ProbDecay; } BurnedNIdV = new Vector<V>(BurnedNIdH.size()); for (int i = 0; i < BurnedNIdH.size(); i++) { BurnedNIdV.add(BurnedNIdH.get(i)); } FwdBurnProb = OldFwdBurnProb; BckBurnProb = OldBckBurnProb; } public void Infect(V Node) { InfectNIdV = new Vector<V>(1); InfectNIdV.add(Node); } public void Infect(Vector<V> Node) { InfectNIdV = new Vector<V>(1); for (int i = 0; i < Node.size(); i++) { InfectNIdV.add(Node.get(i)); } } }