import * as $N from "@/core/base/BaseNode" ; import * as $E from "@/core/base/BaseEdge" ; import * as $G from "@/core/base/BaseGraph" ; import * as uuid from "uuid" ; const v4 = uuid . v4 ; /**
 * EITHER generate new edges via specified degree span
 * OR via probability of edge creation from a specified
 * set of nodes to all others
 */ export interface NodeDegreeConfiguration { und_degree ? : number ; dir_degree ? : number ; min_und_degree ? : number ; max_und_degree ? : number ; min_dir_degree ? : number ; max_dir_degree ? : number ; probability_dir ? : number ; probability_und ? : number ; } export interface ISimplePerturber { createEdgesProb ( probability , directed ? , setOfNodes ? ) : void ; createEdgesSpan ( min , max , directed ? , setOfNodes ? ) : void ; addNodesPercentage ( percentage , config ? ) : void ; addNodesAmount ( amount , config ? ) : void ; addUndEdgesPercentage ( percentage ) : void ; addDirEdgesPercentage ( percentage ) : void ; addEdgesAmount ( amount , config ? ) : void ; deleteNodesPercentage ( percentage ) : void ; deleteUndEdgesPercentage ( percentage ) : void ; deleteDirEdgesPercentage ( percentage ) : void ; deleteNodesAmount ( amount ) : void ; deleteUndEdgesAmount ( amount ) : void ; deleteDirEdgesAmount ( amount ) : void ; } /**
 *
 */ class SimplePerturber implements ISimplePerturber { constructor ( private _graph ) { } param percentage deleteNodesPercentage ( percentage ) { if ( percentage < 0 ) { throw new Error ( "Cowardly refusing to remove a negative amount of nodes" ) ; } if ( percentage > 100 ) { percentage = 100 ; } let nr_nodes_to_delete = Math . ceil ( ( this . _graph . nrNodes ( ) * percentage ) / 100 ) ; this . deleteNodesAmount ( nr_nodes_to_delete ) ; } param percentage deleteUndEdgesPercentage ( percentage ) { if ( percentage > 100 ) { percentage = 100 ; } let nr_edges_to_delete = Math . ceil ( ( this . _graph . nrUndEdges ( ) * percentage ) / 100 ) ; this . deleteUndEdgesAmount ( nr_edges_to_delete ) ; } param percentage deleteDirEdgesPercentage ( percentage ) { if ( percentage > 100 ) { percentage = 100 ; } let nr_edges_to_delete = Math . ceil ( ( this . _graph . nrDirEdges ( ) * percentage ) / 100 ) ; this . deleteDirEdgesAmount ( nr_edges_to_delete ) ; } /**
     *
     */ deleteNodesAmount ( amount ) { if ( amount < 0 ) { throw "Cowardly refusing to remove a negative amount of nodes" ; } if ( this . _graph . nrNodes ( ) === 0 ) { return ; } for ( let nodeID = 0 , randomNodes = this . _graph . pickRandomProperties ( this . _graph . getNodes ( ) , amount ) ; nodeID < randomNodes . length ; nodeID ++ ) { this . _graph . deleteNode ( this . _graph . getNodes ( ) [ randomNodes [ nodeID ] ] ) ; } } /**
     *
     */ deleteUndEdgesAmount ( amount ) { if ( amount < 0 ) { throw "Cowardly refusing to remove a negative amount of edges" ; } if ( this . _graph . nrUndEdges ( ) === 0 ) { return ; } for ( let edgeID = 0 , randomEdges = this . _graph . pickRandomProperties ( this . _graph . getUndEdges ( ) , amount ) ; edgeID < randomEdges . length ; edgeID ++ ) { this . _graph . deleteEdge ( this . _graph . getUndEdges ( ) [ randomEdges [ edgeID ] ] ) ; } } /**
     *
     */ deleteDirEdgesAmount ( amount ) { if ( amount < 0 ) { throw "Cowardly refusing to remove a negative amount of edges" ; } if ( this . _graph . nrDirEdges ( ) === 0 ) { return ; } for ( let edgeID = 0 , randomEdges = this . _graph . pickRandomProperties ( this . _graph . getDirEdges ( ) , amount ) ; edgeID < randomEdges . length ; edgeID ++ ) { this . _graph . deleteEdge ( this . _graph . getDirEdges ( ) [ randomEdges [ edgeID ] ] ) ; } } /**
     *
     */ addUndEdgesPercentage ( percentage ) { let nr_und_edges_to_add = Math . ceil ( ( this . _graph . nrUndEdges ( ) * percentage ) / 100 ) ; this . addEdgesAmount ( nr_und_edges_to_add , { directed : false } ) ; } /**
     *
     */ addDirEdgesPercentage ( percentage ) { let nr_dir_edges_to_add = Math . ceil ( ( this . _graph . nrDirEdges ( ) * percentage ) / 100 ) ; this . addEdgesAmount ( nr_dir_edges_to_add , { directed : true } ) ; } /**
     *
     * DEFAULT edge direction: UNDIRECTED
     */ addEdgesAmount ( amount , config ? ) { if ( amount <= 0 ) { throw new Error ( "Cowardly refusing to add a non-positive amount of edges" ) ; } let node_a , node_b , nodes ; let direction = config && config . directed ? config . directed : false , dir = direction ? "_d" : "_u" ; while ( amount > 0 ) { node_a = this . _graph . getRandomNode ( ) ; while ( ( node_b = this . _graph . getRandomNode ( ) ) === node_a ) { } let edge_id = `${ node_a . getID ( ) }_${ node_b . getID ( ) }${ dir }` ; if ( node_a . hasEdgeID ( edge_id ) ) { continue ; } else { /**
                 * Enable random weights for edges ??
                 */ this . _graph . addEdgeByID ( edge_id , node_a , node_b , { directed : direction , } ) ; -- amount ; } } } /**
     *
     */ addNodesPercentage ( percentage , config ? ) { if ( percentage < 0 ) { throw "Cowardly refusing to add a negative amount of nodes" ; } let nr_nodes_to_add = Math . ceil ( ( this . _graph . nrNodes ( ) * percentage ) / 100 ) ; this . addNodesAmount ( nr_nodes_to_add , config ) ; } todo addNodesAmount ( amount , config ? ) { if ( amount < 0 ) { throw "Cowardly refusing to add a negative amount of nodes" ; } let new_nodes = { } ; while ( -- amount >= 0 ) { let new_node_id = v4 ( ) ; new_nodes [ new_node_id ] = this . _graph . addNodeByID ( new_node_id ) ; } if ( config == null ) { return ; } else { this . createEdgesByConfig ( config , new_nodes ) ; } } /**
     * Go through the degree_configuration provided and create edges
     * as requested by config
     */ private createEdgesByConfig ( config , new_nodes ) { let degree , min_degree , max_degree , deg_probability ; if ( config . und_degree != null || config . dir_degree != null || ( config . min_und_degree != null && config . max_und_degree != null ) || ( config . min_dir_degree != null && config . max_dir_degree != null ) ) { if ( ( degree = config . und_degree ) != null ) { this . createEdgesSpan ( degree , degree , false , new_nodes ) ; } else if ( ( min_degree = config . min_und_degree ) != null && ( max_degree = config . max_und_degree ) != null ) { this . createEdgesSpan ( min_degree , max_degree , false , new_nodes ) ; } if ( ( degree = config . dir_degree ) ) { this . createEdgesSpan ( degree , degree , true , new_nodes ) ; } else if ( ( min_degree = config . min_dir_degree ) != null && ( max_degree = config . max_dir_degree ) != null ) { this . createEdgesSpan ( min_degree , max_degree , true , new_nodes ) ; } } else { if ( config . probability_dir != null ) { this . createEdgesProb ( config . probability_dir , true , new_nodes ) ; } if ( config . probability_und != null ) { this . createEdgesProb ( config . probability_und , false , new_nodes ) ; } } } param probability param directed param new_nodes createEdgesProb ( probability , directed ? , new_nodes ? ) { if ( 0 > probability || 1 < probability ) { throw new Error ( "Probability out of range." ) ; } directed = directed || false ; new_nodes = new_nodes || this . _graph . getNodes ( ) ; let all_nodes = this . _graph . getNodes ( ) , node_a , node_b , edge_id , dir = directed ? "_d" : "_u" ; for ( node_a in new_nodes ) { for ( node_b in all_nodes ) { if ( node_a !== node_b && Math . random ( ) <= probability ) { edge_id = all_nodes [ node_a ] . getID ( ) + "_" + all_nodes [ node_b ] . getID ( ) + dir ; this . _graph . addEdgeByID ( edge_id , all_nodes [ node_a ] , all_nodes [ node_b ] , { directed : directed } ) ; } } } } /**
     * Simple edge generator:
     * Go through all nodes, and
     * add [min, max] (un)directed edges to
     * a randomly chosen node
     * CAUTION: this algorithm could take quadratic runtime in #nodes
     * but should be much faster
     */ createEdgesSpan ( min , max , directed ? , setOfNodes ? ) { if ( min < 0 ) { throw new Error ( "Minimum degree cannot be negative." ) ; } if ( max >= this . _graph . nrNodes ( ) ) { throw new Error ( "Maximum degree exceeds number of reachable nodes." ) ; } if ( min > max ) { throw new Error ( "Minimum degree cannot exceed maximum degree." ) ; } directed = directed || false ; var min = min | 0 , max = max | 0 , new_nodes = setOfNodes || this . _graph . getNodes ( ) , all_nodes = this . _graph . getNodes ( ) , idx_a , node_a , node_b , edge_id , node_keys = Object . keys ( all_nodes ) , keys_len = node_keys . length , rand_idx , rand_deg , dir = directed ? "_d" : "_u" ; for ( idx_a in new_nodes ) { node_a = new_nodes [ idx_a ] ; rand_idx = 0 ; rand_deg = ( Math . random ( ) * ( max - min ) + min ) | 0 ; while ( rand_deg ) { rand_idx = ( keys_len * Math . random ( ) ) | 0 ; node_b = all_nodes [ node_keys [ rand_idx ] ] ; if ( node_a !== node_b ) { edge_id = node_a . getID ( ) + "_" + node_b . getID ( ) + dir ; if ( node_a . hasEdgeID ( edge_id ) ) { continue ; } this . _graph . addEdgeByID ( edge_id , node_a , node_b , { directed : directed , } ) ; -- rand_deg ; } } } } } export { SimplePerturber } ;