/**
* Copyright (c) 2002-2011 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.graphalgo.benchmark.graphgeneration;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import org.neo4j.api.core.Direction;
import org.neo4j.api.core.NeoService;
import org.neo4j.api.core.Node;
import org.neo4j.api.core.Relationship;
import org.neo4j.api.core.RelationshipType;
import org.neo4j.api.core.Transaction;
import org.neo4j.graphalgo.benchmark.graphgeneration.GraphStore.Graph;
public class SmallWorldGraph implements GeneratedGraph
{
private long[] nodes;
private final NeoService neo;
RelationshipType relationshipType;
int numberOfNodes;
int numberOfRelationshipsFromEachNode;
private final Random random;
Transaction transaction;
int transactionCount = 0;
double rewireFactor;
boolean rewireReplaces = true;
boolean distanceRelativeRewiring = false;
protected void renewTransaction()
{
if ( ++transactionCount > 1000 )
{
transactionCount = 0;
transaction.success();
transaction.finish();
transaction = neo.beginTx();
}
}
public Node getRandomNode( Node butNotThisOne )
{
while ( true )
{
long id = nodes[random.nextInt( nodes.length )];
if ( butNotThisOne != null && id == butNotThisOne.getId() )
{
continue;
}
return neo.getNodeById( id );
}
}
public Node getNodeByInternalId( long id )
{
return neo.getNodeById( nodes[(int) id] );
}
public Set<Relationship> getRelationships()
{
Set<Relationship> result = new HashSet<Relationship>();
for ( long nodeId : nodes )
{
Node node = neo.getNodeById( nodeId );
Iterable<Relationship> relationships = node
.getRelationships( Direction.OUTGOING );
for ( Relationship relationship : relationships )
{
result.add( relationship );
}
}
return result;
}
public Set<Node> getNodes()
{
Set<Node> result = new HashSet<Node>();
for ( long nodeId : nodes )
{
result.add( neo.getNodeById( nodeId ) );
}
return result;
}
/**
* @param neo
* @param graphStore
* @param relationshipType
* @param numberOfNodes
* @param numberOfRelationshipsFromEachNode
* The out-degree. I.e. the average node degree will be twice
* this number.
* @param rewireFactor
* Probability for each edge to be connected to some "far-away"
* node.
* @param rewireReplaces
* If true, long distance connections replaces neighbor
* connections. If false neighbor connections are also made.
* @param distanceRelativeRewiring
* If true, long distance edges are draw with probability
* depending on distance. If false, edges are made with uniform
* probability.
*/
public SmallWorldGraph( NeoService neo, GraphStore graphStore,
RelationshipType relationshipType, int numberOfNodes,
int numberOfRelationshipsFromEachNode, double rewireFactor,
boolean rewireReplaces, boolean distanceRelativeRewiring )
{
super();
this.neo = neo;
this.numberOfNodes = numberOfNodes;
this.numberOfRelationshipsFromEachNode = numberOfRelationshipsFromEachNode;
this.relationshipType = relationshipType;
this.rewireFactor = rewireFactor;
this.rewireReplaces = rewireReplaces;
this.distanceRelativeRewiring = distanceRelativeRewiring;
this.random = new Random( System.currentTimeMillis() );
String ident = "SmallWorldGraph," + relationshipType + ","
+ numberOfNodes + "," + numberOfRelationshipsFromEachNode + ","
+ rewireFactor + "," + rewireReplaces + ","
+ distanceRelativeRewiring;
if ( graphStore != null )
{
Graph graph = graphStore.loadGraph( ident );
if ( graph != null )
{
System.out.println( "Loading graph" );
this.nodes = graph.loadNodeIds();
}
}
// Generate if needed
if ( this.nodes == null )
{
Generate();
if ( graphStore != null )
{
graphStore.createGraph( ident ).saveNodeIds( nodes );
}
}
}
public void Generate()
{
transaction = neo.beginTx();
System.out.println( "Generating graph" );
nodes = new long[numberOfNodes];
ProgressBar progressBar = new ProgressBar( numberOfNodes );
for ( int n = 0; n < numberOfNodes; ++n )
{
nodes[n] = neo.createNode().getId();
renewTransaction();
progressBar.Print();
}
progressBar = new ProgressBar( numberOfNodes );
for ( int n = 0; n < numberOfNodes; ++n )
{
Node node = getNodeByInternalId( n );
for ( int i = 0; i < numberOfRelationshipsFromEachNode; ++i )
{
Node node2 = null;
if ( random.nextDouble() < rewireFactor )
{
if ( !distanceRelativeRewiring )
{
node2 = getRandomNode( node );
}
else
{
// this generates a random k where 1 <= k <=
// numberOfNodes/2-1
double r = random.nextDouble()
* Math.log( numberOfNodes / 2 );
int k = (int) Math.exp( r );
node2 = getNodeByInternalId( (n + k)
% (numberOfNodes / 2) );
}
}
if ( node2 != null )
{
node.createRelationshipTo( node2, relationshipType );
renewTransaction();
}
if ( node2 == null || !rewireReplaces )
{
node2 = getNodeByInternalId( (n + i + 1) % numberOfNodes );
node.createRelationshipTo( node2, relationshipType );
renewTransaction();
}
}
progressBar.Print();
}
transaction.success();
transaction.finish();
}
}