import { ITypedNode , TypedNode } from "./TypedNode" ; import { ITypedEdge , TypedEdge , TypedEdgeConfig } from "./TypedEdge" ; import { BaseEdge , IBaseEdge } from "../base/BaseEdge" ; import { BaseGraph } from "../base/BaseGraph" ; import { BaseNode } from "../base/BaseNode" ; import { DIR , ExpansionInbounds , ExpansionInput , ExpansionConfig , ExpansionResult , TypedGraphStats , TypedEdges , TypedNodes , } from "../interfaces" ; import { GENERIC_TYPES } from "@/config/run_config" ; description description todo description solution todo todo todo todo export class TypedGraph extends BaseGraph { protected _type : string ; /**
     * We don't need an extra array of registered types, since an
     * acceptable recommendation graph will only contain a few single
     * up to a few dozen different types, which are quickly obtained
     * via Object.keys()
     */ protected _typedNodes : TypedNodes = new Map ( ) ; protected _typedEdges : TypedEdges = new Map ( ) ; constructor ( public _label ) { super ( _label ) ; this . _type = GENERIC_TYPES . Graph ; this . _typedNodes . set ( GENERIC_TYPES . Node , new Map ( ) ) ; this . _typedEdges . set ( GENERIC_TYPES . Edge , new Map ( ) ) ; } /**
     * convenience methods
     */ n ( id ) { return this . getNodeById ( id ) ; } get type ( ) : string { return this . _type ; } nodeTypes ( ) { return Array . from ( this . _typedNodes . keys ( ) ) ; } edgeTypes ( ) { return Array . from ( this . _typedEdges . keys ( ) ) ; } nrTypedNodes ( type ) { type = type . toUpperCase ( ) ; return this . _typedNodes . get ( type ) ? this . _typedNodes . get ( type ) . size : null ; } nrTypedEdges ( type ) { type = type . toUpperCase ( ) ; return this . _typedEdges . get ( type ) ? this . _typedEdges . get ( type ) . size : null ; } /**
     * Neighbor nodes depending on type
     */ ins ( node , type ) { const targets = node . ins ( type ) ; if ( targets ) { return new Set ( [ ... targets ] . map ( uid => this . n ( TypedNode . nIDFromUID ( uid ) ) as TypedNode ) ) ; } } outs ( node , type ) { const targets = node . outs ( type ) ; if ( targets ) { } return new Set ( [ ... targets ] . map ( uid => this . n ( TypedNode . nIDFromUID ( uid ) ) as TypedNode ) ) ; } unds ( node , type ) { const targets = node . unds ( type ) ; if ( targets ) { return new Set ( [ ... targets ] . map ( uid => this . n ( TypedNode . nIDFromUID ( uid ) ) as TypedNode ) ) ; } } todo static convertToExpansionResult ( input ) { if ( BaseNode . isTyped ( input ) ) { return { set : new Set ( [ input ] ) , freq : new Map < ITypedNode , number > ( ) } ; } else if ( input instanceof Set ) { return { set : input as Set < ITypedNode > , freq : new Map < ITypedNode , number > ( ) , } ; } else { return input as ExpansionResult ; } } description description description todo todo TODO TODO expand ( input , dir , type ) { const nodes = TypedGraph . convertToExpansionResult ( input ) ; const resultSet = new Set < ITypedNode > ( ) ; const freqMap = new Map < ITypedNode , number > ( ) ; for ( let node of nodes . set ) { const targets = node [ dir ] ( type ) ; if ( ! targets ) { continue ; } for ( let target of targets ) { let nodeRef = this . n ( TypedNode . nIDFromUID ( target ) ) as TypedNode ; if ( ! freqMap . has ( nodeRef ) ) { if ( nodes . freq . get ( node ) ) { freqMap . set ( nodeRef , nodes . freq . get ( node ) ) ; } else { freqMap . set ( nodeRef , 1 ) ; } } if ( resultSet . has ( nodeRef ) ) { freqMap . set ( nodeRef , freqMap . get ( nodeRef ) + nodes . freq . get ( node ) ) ; } resultSet . add ( nodeRef ) ; } } return { set : resultSet , freq : freqMap } ; } description expandK ( input , dir , type , cfg = { } ) { if ( cfg . k < 0 ) { throw new Error ( "cowardly refusing to expand a negative number of steps." ) ; } let k = cfg . k && cfg . k < this . _nr_nodes ? cfg . k : this . _nr_nodes - 1 ; let nodes = TypedGraph . convertToExpansionResult ( input ) ; let resultSet = new Set < ITypedNode > ( ) ; const freqMap = new Map < ITypedNode , number > ( ) ; while ( k -- ) { nodes = this . expand ( nodes , dir , type ) ; for ( let nodeRef of nodes . set ) { if ( ! freqMap . has ( nodeRef ) ) { freqMap . set ( nodeRef , nodes . freq . get ( nodeRef ) ) ; } if ( resultSet . has ( nodeRef ) ) { freqMap . set ( nodeRef , freqMap . get ( nodeRef ) + nodes . freq . get ( nodeRef ) ) ; } resultSet . add ( nodeRef ) ; } } return { set : resultSet , freq : freqMap } ; } description peripheryAtK ( input , dir , type , cfg = { } ) { if ( cfg . k < 0 ) { throw new Error ( "cowardly refusing to expand a negative number of steps." ) ; } let nodes = TypedGraph . convertToExpansionResult ( input ) ; let k = cfg . k && cfg . k < this . _nr_nodes ? cfg . k : this . _nr_nodes - 1 ; for ( let it = 0 ; it < k ; it ++ ) { nodes = this . expand ( nodes , dir , type ) ; } return nodes ; } /**
     * TYPED HISTOGRAMS
     */ inHistT ( nType , eType ) { return this . degreeHistT ( DIR . in , nType , eType ) ; } outHistT ( nType , eType ) { return this . degreeHistT ( DIR . out , nType , eType ) ; } connHistT ( nType , eType ) { return this . degreeHistT ( DIR . und , nType , eType ) ; } private degreeHistT ( dir , nType , eType ) { let result = [ ] ; for ( let [ node_id , node ] of this . _typedNodes . get ( nType ) ) { let deg ; switch ( dir ) { case DIR . in : deg = node . ins ( eType ) ? node . ins ( eType ) . size : 0 ; break ; case DIR . out : deg = node . outs ( eType ) ? node . outs ( eType ) . size : 0 ; break ; default : deg = node . unds ( eType ) ? node . unds ( eType ) . size : 0 ; } if ( ! result [ deg ] ) { result [ deg ] = new Set ( [ node ] ) ; } else { result [ deg ] . add ( node ) ; } } return result ; } todo param id param opts addNodeByID ( id , opts ? ) { if ( this . hasNodeID ( id ) ) { throw new Error ( "Won't add node with duplicate ID." ) ; } let node = new TypedNode ( id , opts ) ; return this . addNode ( node ) ? node : null ; } addNode ( node ) { if ( ! super . addNode ( node ) ) { return null ; } const id = node . getID ( ) , type = node . type ? node . type . toUpperCase ( ) : null ; /**
         *  Untyped nodes will be treated as `generic` type
         */ if ( ! type ) { this . _typedNodes . get ( GENERIC_TYPES . Node ) . set ( id , node ) ; } else { if ( ! this . _typedNodes . get ( type ) ) { this . _typedNodes . set ( type , new Map ( ) ) ; } this . _typedNodes . get ( type ) . set ( id , node ) ; } return node ; } getNodeById ( id ) { return super . getNodeById ( id ) as TypedNode ; } getNodesT ( type ) { return this . _typedNodes . get ( type . toUpperCase ( ) ) ; } getEdgesT ( type ) { return this . _typedEdges . get ( type . toUpperCase ( ) ) ; } deleteNode ( node ) { const id = node . getID ( ) , type = node . type ? node . type . toUpperCase ( ) : GENERIC_TYPES . Node ; if ( ! this . _typedNodes . get ( type ) ) { throw Error ( "Node type does not exist on this TypedGraph." ) ; } const removeNode = this . _typedNodes . get ( type ) . get ( id ) ; if ( ! removeNode ) { throw Error ( "This particular node is nowhere to be found in its typed set." ) ; } this . _typedNodes . get ( type ) . delete ( id ) ; if ( this . nrTypedNodes ( type ) === 0 ) { this . _typedNodes . delete ( type ) ; } super . deleteNode ( node ) ; } addEdgeByID ( id , a , b , opts ? ) { let edge = new TypedEdge ( id , a , b , opts || { } ) ; return this . addEdge ( edge ) ; } addEdge ( edge ) { if ( ! super . addEdge ( edge ) ) { return null ; } const id = edge . getID ( ) ; let type = GENERIC_TYPES . Edge ; if ( BaseEdge . isTyped ( edge ) && edge . type ) { type = edge . type . toUpperCase ( ) ; } /**
         *  Same procedure as every node...
         */ if ( id === type ) { this . _typedEdges . get ( GENERIC_TYPES . Edge ) . set ( id , edge as ITypedEdge ) ; } else { if ( ! this . _typedEdges . get ( type ) ) { this . _typedEdges . set ( type , new Map ( ) ) ; } this . _typedEdges . get ( type ) . set ( id , edge as ITypedEdge ) ; } return edge as ITypedEdge ; } deleteEdge ( edge ) { const id = edge . getID ( ) ; let type = GENERIC_TYPES . Edge ; if ( BaseEdge . isTyped ( edge ) && edge . type ) { type = edge . type . toUpperCase ( ) ; } if ( ! this . _typedEdges . get ( type ) ) { throw Error ( "Edge type does not exist on this TypedGraph." ) ; } const removeEdge = this . _typedEdges . get ( type ) . get ( id ) ; if ( ! removeEdge ) { throw Error ( "This particular edge is nowhere to be found in its typed set." ) ; } this . _typedEdges . get ( type ) . delete ( id ) ; if ( this . nrTypedEdges ( type ) === 0 ) { this . _typedEdges . delete ( type ) ; } super . deleteEdge ( edge ) ; } getStats ( ) { let typed_nodes = { } , typed_edges = { } ; this . _typedNodes . forEach ( ( k , v ) => ( typed_nodes [ v ] = k . size ) ) ; this . _typedEdges . forEach ( ( k , v ) => ( typed_edges [ v ] = k . size ) ) ; return { ... super . getStats ( ) , typed_nodes , typed_edges , } ; } }